... Al final de la publicación, sobre una nueva estrategia para contratar especialistas. Hubo un momento en que la tecnología de la información de Alfa-Bank se concentraba principalmente en tres ciudades: Moscú, San Petersburgo y Ekaterimburgo. Ahora podemos mirar a personas de todo el país. Y no solo.
Gracias a todos los que respondieron, hubo más de 1,500 de esas personas, de las cuales 198 llegaron a la final y mostraron los mejores resultados. Sí, no fue fácil: las tareas resultaron ser más difíciles de lo que muchos participantes esperaban, y el formato del campeonato en línea con transmisión de conferencias, francamente, fue tenso. Aún así, 5 horas de programación en línea no es una experiencia retro en un equipo.
En total, tres ganadores que recibieron un premio en efectivo, así como invitaciones de trabajo para nosotros y para los socios del campeonato. Aquí están:
1er lugar: Mikhail Burshtein, Moscú (250,000 rublos)
2do lugar: Alexander Ilyushechkin, Moscú (150,000 rublos)
3er lugar: Pavel Lyubinsky, San Petersburgo (100,000 rublos)
Tareas
# 1 ¿Dónde está el cajero automático?
Detalles de la tarea
-.
REST API IP:8080.
API – api.json.
- API .
: api.alfabank.ru/node/238
API .
: api.alfabank.ru/start
* IP — IP .
* : github.com/evgenyshiryaev/alfa-battle-resources/tree/master/task1
: GET IP:8080/atms/{deviceId}
:
— 200 AtmResponse
— 404 ErrorResponse ( )
:
:
18 .
- — .
3 ! -.
payments=true, (. - API ATMServices).
: GET IP:8080/atms/nearest?latitude=string&longitude=string&payments=boolean
: 200 AtmResponse
:
:
36 .
- .
, .
: GET IP:8080/atms/nearest-with-alfik?latitude=string&longitude=string&alfik=int
: 200 [AtmResponse]
, WebSocket STOMP .
:
( task1):
: ws://IP:8100
: { “deviceId”: 0 }
/topic/alfik: { “deviceId”: 0, “alfik”: 0 }
:
(.. 153463 — 383026 )
:
( , )
Condición
-.
REST API IP:8080.
API – api.json.
- API .
: api.alfabank.ru/node/238
API .
: api.alfabank.ru/start
* IP — IP .
* : github.com/evgenyshiryaev/alfa-battle-resources/tree/master/task1
1. deviceId
: GET IP:8080/atms/{deviceId}
:
— 200 AtmResponse
— 404 ErrorResponse ( )
:
GET http://IP:8080/atms/153463
200
{
"deviceId": 153463,
"latitude": "55.6610213",
"longitude": "37.6309405",
"city": "",
"location": " ., 4, . 10",
"payments": false
}
:
GET http://IP:8080/atms/1
404
{
“status”: “atm not found”
}
18 .
2. (-)
- — .
3 ! -.
payments=true, (. - API ATMServices).
: GET IP:8080/atms/nearest?latitude=string&longitude=string&payments=boolean
: 200 AtmResponse
:
GET http://IP:8080/atms/nearest?latitude=55.66&longitude=37.63
200
{
"deviceId": 153463,
"latitude": "55.6610213",
"longitude": "37.6309405",
"city": "",
"location": " ., 4, . 10",
"payments": false
}
:
GET http://IP:8080/atms/nearest?latitude=55.66&longitude=37.63&payments=true
200
{
"deviceId": 210612,
"latitude": "55.66442",
"longitude": "37.628051",
"city": "",
"location": " , . 14",
"payments": true
}
36 .
3.
- .
, .
: GET IP:8080/atms/nearest-with-alfik?latitude=string&longitude=string&alfik=int
: 200 [AtmResponse]
, WebSocket STOMP .
:
mkdir task1 ; cd task1
wget https://raw.githubusercontent.com/evgenyshiryaev/alfa-battle-resources/master/task1/docker-compose.yml
docker-compose up -d
( task1):
docker-compose down
: ws://IP:8100
: { “deviceId”: 0 }
/topic/alfik: { “deviceId”: 0, “alfik”: 0 }
:
GET http://IP:8080/atms/nearest-with-alfik?latitude=55.66&longitude=37.63&alfik=300000
200
[{
"deviceId": 153463,
"latitude": "55.6610213",
"longitude": "37.6309405",
"city": "",
"location": " ., 4, . 10",
"payments": false
}]
(.. 153463 — 383026 )
:
GET http://IP:8080/atms/nearest-with-alfik?latitude=55.66&longitude=37.63&alfik=400000
200
[{
"deviceId": 153463,
"latitude": "55.6610213",
"longitude": "37.6309405",
"city": "",
"location": " ., 4, . 10",
"payments": false
},
{
“deviceId": 153465,
"latitude": "55.6602801",
"longitude": "37.633823",
"city": "",
"location":" ., 18",
"payments": false
}]
( , )
# 2 Analízalo
Detalles de la tarea
Kafka RAW_PAYMENTS .
docker IP:29092 .
Kafka.
:
.
( task2):
* IP — IP .
* : github.com/evgenyshiryaev/alfa-battle-resources/tree/master/task2
:
, key1 — .
.
:
RAW_PAYMENTS. .
, .
, Kafka .
.
, , . , ( , - ).
:
start.sh, . ,
Swagger- api-swagger.json .
:
( categoryId).
, .
.
200
:
:
analyticInfo Id .
— 200
— 404 + {«status»:”user not found"} ( )
:
:
40 .
. :
— 200
— 404 + {«status»:”user not found"} ( )
.
:
60
.
:
, .
— 200
— 404 + {«status»:”user not found"} ( )
, .
:
Kafka RAW_PAYMENTS .
docker IP:29092 .
Kafka.
:
mkdir task2 ; cd task2
wget https://raw.githubusercontent.com/evgenyshiryaev/alfa-battle-resources/master/task2/data.txt
wget https://raw.githubusercontent.com/evgenyshiryaev/alfa-battle-resources/master/task2/docker-compose.yml
wget https://raw.githubusercontent.com/evgenyshiryaev/alfa-battle-resources/master/task2/start.sh
bash start.sh
.
( task2):
docker-compose down
* IP — IP .
* : github.com/evgenyshiryaev/alfa-battle-resources/tree/master/task2
:
key1:{"ref":"U030306190000188", "categoryId":1, "userId":"XAABAA", "recipientId":"XA3SZV", "desc":" ", "amount":10.0}
, key1 — .
.
:
- Kafka.
- .
- REST API . 8081.
RAW_PAYMENTS. .
, .
, Kafka .
.
, , . , ( , - ).
:
- consumer-group-id. .
- consumer-group. , , ,
docker exec -i broker kafka-consumer-groups --bootstrap-server broker:9092 --all-groups --all-topics --reset-offsets --to-earliest --execute
start.sh, . ,
Swagger-
Swagger- api-swagger.json .
:
key1:{"ref":"ref1", "categoryId":1, "userId":"User_1", "recipientId":"User_2", "desc":" _1", "amount":10.0}
key2:{"ref":"ref2", "categoryId":2, "userId":"User_1", "recipientId":"User_2", "desc":" _2", "amount":350.56}
key3:{"ref":"ref3", "categoryId":1, "userId":"User_1", "recipientId":"User_2", "desc":" _3", "amount":700.0}
key4:{"ref":"ref4", "categoryId":3, "userId":"User_1", "recipientId":"User_2", "desc":" _4", "amount":5.99}
key5:{"ref":"ref5", "categoryId":1, "userId":"User_1", "recipientId":"User_2", "desc":" _5", "amount":10.0}
key6:{"ref":"ref6", "categoryId":2, "userId":"User_2", "recipientId":"User_3", "desc":" _6", "amount":350.56}
key7:{"ref":"ref7", "categoryId":1, "userId":"User_1", "recipientId":"User_2", "desc":" _7", "amount":890.0}
key8:{"ref":"ref8", "categoryId":3, "userId":"User_3", "recipientId":"User_2", "desc":" _8", "amount":35.99}
key9:{"ref":"ref9", "categoryId":1, "userId":"User_1", "recipientId":"User_2", "desc":" _9", "amount":890.0}
key10:{"ref":"ref10", "categoryId":3, "userId":"User_3", "recipientId":"User_2", "desc":" _10", "amount":35.9910}
key11:{"ref":"ref11", "categoryId":1, "userId":"User_1", "recipientId":"User_2", "desc":" _11", "amount":10.0}
key12:{"ref":"ref12", "categoryId":2, "userId":"User_2", "recipientId":"User_3", "desc":" _12", "amount":350.56}
key13:{"ref":"ref13", "categoryId":1, "userId":"User_1", "recipientId":"User_2", "desc":" _13", "amount":10.0}
key14:{"ref":"ref14", "categoryId":2, "userId":"User_2", "recipientId":"User_3", "desc":" _14", "amount":350.56}
key15:{"ref":"ref15", "categoryId":4, "userId":"User_1", "recipientId":"User_4", "desc":" _15", "amount":15.00}
1.
( categoryId).
, .
.
GET IP:8081/admin/health
200
:
{"status":"UP"}
<h4>GET http://IP:8081/analytic</h4>
200
:
[
{
"userId": "User_3",
"totalSum": 71.981,
"analyticInfo": {
"3": {
"min": 35.99,
"max": 35.991,
"sum": 71.981
}
}
},
{
"userId": "User_2",
"totalSum": 1051.68,
"analyticInfo": {
"2": {
"min": 350.56,
"max": 350.56,
"sum": 1051.68
}
}
},
{
"userId": "User_1",
"totalSum": 2891.55,
"analyticInfo": {
"1": {
"min": 10,
"max": 890,
"sum": 2520
},
"2": {
"min": 350.56,
"max": 350.56,
"sum": 350.56
},
"3": {
"min": 5.99,
"max": 5.99,
"sum": 5.99
},
"4": {
"min": 15,
"max": 15,
"sum": 15
}
}
}
]
analyticInfo Id .
GET IP:8081/analytic/{userId}
— 200
— 404 + {«status»:”user not found"} ( )
:
GET http://IP:8081/analytic/User_1
:
{
"userId": "User_1",
"totalSum": 2891.55,
"analyticInfo": {
"1": {
"min": 10,
"max": 890,
"sum": 2520
},
"2": {
"min": 350.56,
"max": 350.56,
"sum": 350.56
},
"3": {
"min": 5.99,
"max": 5.99,
"sum": 5.99
},
"4": {
"min": 15,
"max": 15,
"sum": 15
}
}
}
40 .
2.
. :
GET IP:8081/analytic/{userId}/stats
— 200
— 404 + {«status»:”user not found"} ( )
.
:
GET http://IP:8081/analytic/User_1/stats
:
{
"oftenCategoryId": 1,
"rareCategoryId": 2,
"maxAmountCategoryId": 1,
"minAmountCategoryId": 3
}
60
3.
.
:
- , , (recipientId userId )
, .
GET IP:8081/analytic/{userId}/templates
— 200
— 404 + {«status»:”user not found"} ( )
, .
:
GET http://IP:8081/analytic/User_1/templates
:
[
{
"recipientId": "User_2",
"categoryId": 1,
"amount": 10
}
]
# 3 Caja libre
Detalles de la tarea
- .
REST API IP:8082.
API – api.json.
, , , , .
PostgreSQL.
PostgreSQL:
( task3):
: IP:5432
DB: alfa_battle
Auth: alfa_battle / qwe123
* IP — IP .
* : github.com/evgenyshiryaev/alfa-battle-resources/tree/master/task3
: GET IP:8082/branches/{id}
:
— 200 Branches
— 404 ErrorResponse ( )
:
:
8 .
-, , 1.
.
, Latitude Longitude.
: GET IP:8082/branches/lat=string&lon=string
: 200 Branches
:
Distance — , , .
28 .
, — , , , , .
.
: GET IP:8082/branches/{id}/predict?dayOfWeek=int&hourOfDay=int
:
— 200 BranchesWithPredicting
— 404 ErrorResponse ( )
:
dayOfWeek — , 1, 7
hourOfDay — 0 23
Predicting — , , .
:
.
, , apache-commons-math3
- .
REST API IP:8082.
API – api.json.
, , , , .
PostgreSQL.
PostgreSQL:
mkdir task3 ; cd task3
wget https://raw.githubusercontent.com/evgenyshiryaev/alfa-battle-resources/master/task3/docker-compose.yml
wget https://raw.githubusercontent.com/evgenyshiryaev/alfa-battle-resources/master/task3/Dockerfile
wget https://raw.githubusercontent.com/evgenyshiryaev/alfa-battle-resources/master/task3/init_db.sql
docker-compose up -d
( task3):
docker-compose down
: IP:5432
DB: alfa_battle
Auth: alfa_battle / qwe123
* IP — IP .
* : github.com/evgenyshiryaev/alfa-battle-resources/tree/master/task3
1. id
: GET IP:8082/branches/{id}
:
— 200 Branches
— 404 ErrorResponse ( )
:
GET http://IP:8082/branches/612
200
{
"id": 612,
"title": "",
"lon": 37.6329,
"lat": 55.7621,
"address": " ., 13, . 1"
}
:
GET http://IP:8082/branches/1
404
{
“status”: “branch not found”
}
8 .
2. (-)
-, , 1.
.
, Latitude Longitude.
: GET IP:8082/branches/lat=string&lon=string
: 200 Branches
:
GET http://IP:8082/branches/lat=55.773284&lon=37.624125
200
{
"id": 631,
"title": " ",
"lon": 37.6227,
"lat": 55.7695,
"address": " ., 16/1",
"distance": 430
}
Distance — , , .
28 .
3.
, — , , , , .
.
: GET IP:8082/branches/{id}/predict?dayOfWeek=int&hourOfDay=int
:
— 200 BranchesWithPredicting
— 404 ErrorResponse ( )
:
GET http://IP:8082/branches/612/predict?dayOfWeek=1&hourOfDay=14
200
{
"id": 612,
"title": "",
"lon": 37.6329,
"lat": 55.7621,
"address": " ., 13, . 1",
"dayOfWeek": 1,
"hourOfDay": 14,
"predicting": 117
}
dayOfWeek — , 1, 7
hourOfDay — 0 23
Predicting — , , .
:
GET http://IP:8082/branches/1/predict?dayOfWeek=1&hourOfDay=14
404
{
“status”: “branch not found”
}
.
, , apache-commons-math3
# 4 Préstamos elásticos
Detalles de la tarea
1 2 JSON:
person.json — , :
, :
ID — ,
DocId — ( 9 )
FIO —
Birthday — MM/dd/yyyy
Salary — , , .. 201.02 20102 .
Gender —
loans.json — , :
, :
Loan — ,
PersonId —
Amount — , , .. 201.02 20 102
StartDate — MM/dd/yyyy
Period —
, ElasticSearch .
REST- ElasticSearch, . IP:8083
ElasticSearch:
ElasticSearch IP:9200
( task4):
* IP — IP .
* : github.com/evgenyshiryaev/alfa-battle-resources/tree/master/task4
ElasticSearch :
persons
loans
GET IP:8083/admin/health
200
{«status»:«UP»}
ElasicSearch.
POST IP:8083/loans/loadPersons
200
{«status»:«OK»}
ElasicSearch.
, Document, ElasticSearch .
POST IP:8083/loans/loadLoans
200
{«status»:«OK»}
400 ( )
{
«status»: «person not found»
}
400 ( )
, — Document.
, :
countLoan —
sumAmountLoans —
loans —
, ( ElasticSearch).
1 2 JSON:
person.json — , :
{
"ID":"29",
"DocId":"702821510",
"FIO":"Phoebe Whitehouse",
"Birthday":"7/12/1971",
"Salary":"201.02",
"Gender":"F"
}
, :
ID — ,
DocId — ( 9 )
FIO —
Birthday — MM/dd/yyyy
Salary — , , .. 201.02 20102 .
Gender —
loans.json — , :
{
"Loan":"631553",
"PersonId":"68",
"Amount":"201.02",
"StartDate":"6/1/2019",
"Period":"1"
}
, :
Loan — ,
PersonId —
Amount — , , .. 201.02 20 102
StartDate — MM/dd/yyyy
Period —
, ElasticSearch .
REST- ElasticSearch, . IP:8083
ElasticSearch:
mkdir task4 ; cd task4
wget https://raw.githubusercontent.com/evgenyshiryaev/alfa-battle-resources/master/task4/docker-compose.yml
docker-compose up -d
ElasticSearch IP:9200
( task4):
docker-compose down
* IP — IP .
* : github.com/evgenyshiryaev/alfa-battle-resources/tree/master/task4
ElasticSearch :
persons
- Birthday — 1945-05-03
- Salary — ,
loans
- PersonId — , Document, DocId Persons.
- Amount — , .
- StartDate — , 1945-05-03
- Period — ,
GET IP:8083/admin/health
200
{«status»:«UP»}
ElasicSearch.
POST IP:8083/loans/loadPersons
200
{«status»:«OK»}
ElasicSearch.
, Document, ElasticSearch .
POST IP:8083/loans/loadLoans
200
{«status»:«OK»}
GET http://IP:8083/loans/getPerson/855406656/
200
{
"docid": "855406656",
"fio": "Celina Jackson",
"birthday": "1961-05-22",
"salary": 69106.0,
"gender": "F"
}
400 ( )
{
«status»: «person not found»
}
GET http://IP:8083/loans/getLoan/692826/
200
{
"loan": "692826",
"amount": 448900,
"document": "027665876",
"startdate": "2017-01-16",
"period": 48
}
400 ( )
{
"status": "loan not found"
}
, — Document.
GET http://IP:8083/loans/creditHistory/737767072/
200
{
"countLoan": 4,
"sumAmountLoans": 1058400.0,
"loans": [
{
"loan": "434224",
"amount": 7100,
"document": "737767072",
"startdate": "2019-09-18",
"period": 12
},
{
"loan": "917105",
"amount": 283600,
"document": "737767072",
"startdate": "2019-12-22",
"period": 12
},
{
"loan": "692147",
"amount": 300800,
"document": "737767072",
"startdate": "2016-08-01",
"period": 24
},
{
"loan": "145020",
"amount": 466900,
"document": "737767072",
"startdate": "2017-01-16",
"period": 36
}
]
}
, :
countLoan —
sumAmountLoans —
loans —
,
GET http://IP:8083/loans/creditClosed
200
[
{
"loan": "222398",
"amount": 265400,
"document": "074658188",
"startdate": "2017-09-22",
"period": 12
},
"loan": "826942",
"amount": 329400,
"document": "788117788",
"startdate": "2016-01-29",
"period": 48
},
...
]
, ( ElasticSearch).
GET http://IP:8083/loans/loansSortByPersonBirthday
200
[
{
"id": null,
"docid": "840704451",
"fio": "John Isaac",
"birthday": "19.08.1989",
"salary": 58295.0,
"gender": "M",
"loans": [
{
"loan": "771916",
"amount": 337600,
"document": "840704451",
"startdate": "2019-11-09",
"period": 48
},
{
"loan": "504544",
"amount": 358900,
"document": "840704451",
"startdate": "2018-06-10",
"period": 36
},
{
"loan": "699247",
"amount": 464400,
"document": "840704451",
"startdate": "2018-10-30",
"period": 36
},
{
"loan": "783101",
"amount": 139300,
"document": "840704451",
"startdate": "2017-02-19",
"period": 36
}
]
},
{
"id": null,
"docid": "023665566",
"fio": "Denny Tanner",
"birthday": "25.03.1989",
"salary": 80713.0,
"gender": "M",
"loans": [
{
"loan": "631553",
"amount": 403000,
"document": "023665566",
"startdate": "2019-06-01",
"period": 12
},
{
"loan": "598452",
"amount": 198500,
"document": "023665566",
"startdate": "2015-09-28",
"period": 36
},
{
"loan": "151915",
"amount": 13600,
"document": "023665566",
"startdate": "2019-06-15",
"period": 12
},
{
"loan": "368342",
"amount": 350500,
"document": "023665566",
"startdate": "2017-02-06",
"period": 48
},
{
"loan": "633056",
"amount": 482900,
"document": "023665566",
"startdate": "2016-07-01",
"period": 12
}
]
},
...
]
# 5 en la aguja promocional
Detalles de la tarea
.
- .
.
REST API IP:8084 :
: /promo, /receipt.
* IP — IP .
* : github.com/evgenyshiryaev/alfa-battle-resources/tree/master/task5
- , -, .
-, - .
- , -, .
2 RoundingMode.HALF_EVEN.
!!! , code review !!!
:
: POST IP:8084/promo
{}
: 200
: POST IP:8084/receipt
{
«shopId»: 1,
«loyaltyCard»: false,
«positions»:
[
{
«itemId»: «3432166»,
«quantity»: 1
}
]
}
: 200
{
«total»: 141.99,
«discount»: 0.00,
«positions»:
[
{
«id»: «3432166»,
«name»: «...G11 Green .1»,
«price»: 141.99,
«regularPrice»: 141.99
}
]
}
8 .
/promo - “ ” (LoyaltyCardRule). , .
- , /receipt loyaltyCard=true - , id .
:
: POST IP:8084/promo
: 200
: POST IP:8084/receipt
16 .
/promo - LoyaltyCardRule ItemCountRule. , .
N+k , N ID k . N=3, k=2:
.
- .
.
REST API IP:8084 :
- /promo – POST - ( , ). .
- /receipt – , POST id , (, ), , , .
: /promo, /receipt.
* IP — IP .
* : github.com/evgenyshiryaev/alfa-battle-resources/tree/master/task5
- CSV :
- API – api.yml
- , -, .
-, - .
- , -, .
2 RoundingMode.HALF_EVEN.
!!! , code review !!!
1. -
:
: POST IP:8084/promo
{}
: 200
: POST IP:8084/receipt
{
«shopId»: 1,
«loyaltyCard»: false,
«positions»:
[
{
«itemId»: «3432166»,
«quantity»: 1
}
]
}
: 200
{
«total»: 141.99,
«discount»: 0.00,
«positions»:
[
{
«id»: «3432166»,
«name»: «...G11 Green .1»,
«price»: 141.99,
«regularPrice»: 141.99
}
]
}
8 .
2. -
/promo - “ ” (LoyaltyCardRule). , .
- , /receipt loyaltyCard=true - , id .
:
: POST IP:8084/promo
{
"loyaltyCardRules":
[
{
"shopId": -1,
"discount": 0.03
},
{
"shopId": 2,
"discount": 0.05
}
]
}
: 200
: POST IP:8084/receipt
{
"shopId": 1,
"loyaltyCard": false,
"positions":
[
{
"itemId": "3432166",
"quantity": 1
}
]
}
: 200
{
"total": 137.73,
"discount": 4.26,
"positions":
[
{
"id": "3432166",
"name": "...G11 Green .1",
"price": 137.73,
"regularPrice": 141.99
}
]
}
16 .
3. - N+k
/promo - LoyaltyCardRule ItemCountRule. , .
N+k , N ID k . N=3, k=2:
Grabaciones de video de informes
Los 16 informes de nuestros chicos y socios de Beeline y X5 Retail Group se pueden ver en el sitio web del evento .
Entonces, ¿qué pasa con la contratación?
Damir Battulin, nuestro director del departamento de desarrollo de canales online, habló sobre la contratación, las iniciativas estratégicas del banco, los principios del trabajo en equipo en los proyectos y los criterios de elección de nuevos especialistas para el desarrollo de la digitalización.
: , - . , , , , . . , .
. , . , , . , . .
Puede ver todas nuestras vacantes actuales (TI, marketing, RR.HH., etc.) en esta página.
Para los amantes de los detalles: toda la entrevista
- 100 . . , , . , - -, , .
— -, 2021 . . , , — . 7 12 . , , .
, - , . , , , , . , , ( , ): — , , -. , .
— ?
— , -.
— , , .
— . , . , , , .
, , — smart branch. – 400 . , , .
? , . , . , . , . , , .
, . , , , .
, , -. . , , , . , , , .
— ?
— : , - . , , , , . . , .
. , . , , . , . .
— ?
— – time to market. . , . . , , .
, , . smart branch , .
— , ?
— -. , . , . .
. , . — , . , . - , . . , : iOS- Lead product manager iOS, Chief product owner .
. 56, . 60%, 100%.
— ?
— , . , .
, chief product owner. , . – , , . , . , - , , , .
— , . ?
— , , , . , , , . .
. , , .
— ?
— – . . . , , , .
– . , -, . , , . , .
, , , , , . — , , , . .
— -, 2021 . . , , — . 7 12 . , , .
, - , . , , , , . , , ( , ): — , , -. , .
— ?
— , -.
— , , .
— . , . , , , .
, , — smart branch. – 400 . , , .
? , . , . , . , . , , .
, . , , , .
, , -. . , , , . , , , .
— ?
— : , - . , , , , . . , .
. , . , , . , . .
— ?
— – time to market. . , . . , , .
, , . smart branch , .
— , ?
— -. , . , . .
. , . — , . , . - , . . , : iOS- Lead product manager iOS, Chief product owner .
. 56, . 60%, 100%.
— ?
— , . , .
, chief product owner. , . – , , . , . , - , , , .
— , . ?
— , , , . , , , . .
. , , .
— ?
— – . . . , , , .
– . , -, . , , . , .
, , , , , . — , , , . .