Русский
Русский
English
Статистика
Реклама

Features

Новые возможности ES2021 ES12

18.12.2020 00:05:23 | Автор: admin

Ожидается, что версия ECMAScript 2021 будет выпущена в июне 2021 года. Вот некоторые из функций, которые могут оказаться в ES2021 или ES12. Список подготовлен на основе ECMAScript Proposals и новых функций, выпущенных движком Google Chrome V8.

Все функции, перечисленные ниже, на момент написания поддерживаются в сборке Google Chrome Canary (версия браузера Google Chrome, поддерживающая экспериментальные возможности).

Метод String replaceAll()

String.prototype.replaceAll() заменяет все вхождения строки другим строковым значением.

В настоящее время в JavaScriptу строк есть метод replace(). Его можно использовать для замены подстроки другой строкой.

const str = "Backbencher sits at the Back";const newStr = str.replace("Back", "Front");console.log(newStr); // "Frontbencher sits at the Back"

Если входной шаблон для замены является строкой, метод replace() заменяет только первое вхождение. Поэтому в коде второе вхождение Back не заменяется.

Мы можем сделать полную замену, только если предоставим шаблон для замены в виде регулярного выражения.

const str = "Backbencher sits at the Back";const newStr = str.replace(/Back/g, "Front");console.log(newStr); // "Frontbencher sits at the Front"

String.prototype.replaceAll() пытается произвести замену всех вхождений,даже если входной шаблон является строкой.

const str = "Backbencher sits at the Back";const newStr = str.replaceAll("Back", "Front");console.log(newStr); // "Frontbencher sits at the Front"

Приватные методы

Приватные методы могут быть доступны только внутри класса, в котором они определены. Имена приватных методов начинаются с символа #.

class Person {    // Приватный метод    #setType() {        console.log("I am Private");    }    // Публичный метод    show() {        this.#setType();    }}const personObj = new Person();personObj.show(); // "I am Private";personObj.setType(); // TypeError: personObj.setType is not a function

Поскольку setType() является частным методом, personObj.setType возвращает значение undefined. Попытка использовать undefined в качестве функции вызывает ошибку TypeError.

Приватные аксессоры

Функции-аксессоры (get/set) можно сделать приватными, добавив # к имени функции.

class Person {    // Публичные аксессоры    get name() { return "Backbencher" }    set name(value) {}    // Приватные аксессоры    get #age() { return 42 }    set #age(value) {}}

В приведенном выше коде ключевые слова get и set делают name аксессором. Несмотря на то, что name выглядит как функция, его можно читать как обычное свойство.

const obj = new Person();console.log(obj.name); // "Backbencher"console.log(obj.age); // undefined

WeakRef

WeakRef означает слабые ссылки (Weak References). В основном слабые ссылки используются для реализации кэшей или маппингов больших объектов. В таких сценариях мы не хотим удерживать большое количество памяти надолго, сохраняя редко используемый кэш или маппинг. Мы можем разрешить сборку мусора для памяти в ближайшее время, а позже, если она нам снова понадобится, мы можем создать свежий кэш.

JavaScript - это язык со сборкой мусора. Если переменная больше недоступна, сборщик мусора JavaScript автоматически удаляет ее. Вы можете узнать больше о сборке мусора JavaScript здесь, на сайте MDN.

Рассмотрим следующий код:

const callback = () => {    const aBigObj = {        name: "Backbencher"    };    console.log(aBigObj);};(async function(){    await new Promise((resolve) => {        setTimeout(() => {            callback();            resolve();        }, 2000);    });})();

Код может показаться сложным. Но здесь мы всего лишь создаем функцию с именем callback() и выполняем ее с помощью setTimeout(). Асинхронная обёртка предназначена только для использования функции await. await - это функция в ES6, которая помогает синхронно выполнять асинхронный код.

При выполнении указанного выше кода через 2 секунды выводится Backbencher. В зависимости от того, как мы используем функцию callback(), переменная aBigObj может храниться в памяти вечно.

Давайте сделаем aBigObj слабой ссылкой.

const callback = () => {    const aBigObj = new WeakRef({        name: "Backbencher"    });    console.log(aBigObj.deref().name);}(async function(){    await new Promise((resolve) => {        setTimeout(() => {            callback(); // Гарантированно напечатает "Backbencher"            resolve();        }, 2000);    });    await new Promise((resolve) => {        setTimeout(() => {            callback(); // Нет гарантий, что напечатается "Backbencher"            resolve();        }, 5000);    });})();

WeakRef создается с помощью new WeakRef(). Позже ссылка читается с помощью метода .deref(). Внутри асинхронной функции первый setTimeout() обязательно напечатает значение name. Это гарантируется на первом этапе цикла обработки событий после создания слабой ссылки.

Но нет гарантии, что второй setTimeout() напечатает Backbencher. Слабая ссылка могла быть освобождена сборщиком мусора. Поскольку сборка мусора в разных браузерах работает по-разному, мы не можем гарантировать результат. Вот почему мы используем WeakRef в таких ситуациях, как управление кэшем.

Финализаторы

FinalizationRegistry - это дополнительная функция WeakRef. Он позволяет программистам регистрировать коллбеки, которые будут вызываться после того, как объект был забран сборщиком мусора.

const registry = new FinalizationRegistry((value) => {    console.log(value);});

Здесь registry является экземпляром FinalizationRegistry. Функция обратного вызова (коллбек), переданная в FinalizationRegistry, срабатывает при сборке мусора.

(function () {    const obj = {};    registry.register(obj, "Backbencher");})();

Строка 3 привязыват obj к registry. Когда obj забирает сборщик мусора, второй аргумент метода .register() передается функции обратного вызова. Итак, согласно нашей логике, когда obj забирает сборщик мусора, Backbencher передается функции обратного вызова и печатается в консоли.

При выполнении приведенного выше кода в консоли Google Chrome Canary, примерно через 1 минуту, он напечатал Backbencher в консоли. Еще один способ принудительно выполнить сборку мусора в Chrome - щелкнуть на иконку Собрать мусор. Мы можем найти его во вкладке Производительность.

Promise.any() и AggregateError

Promise.any() успешно завершается, если успешно завершился любой из предоставленных в качестве аргументов промис. Ниже у нас есть 3 промиса, которые выполняются в случайное время.

const p1 = new Promise((resolve, reject) => {    setTimeout(() => resolve("A"), Math.floor(Math.random() * 1000));});const p2 = new Promise((resolve, reject) => {    setTimeout(() => resolve("B"), Math.floor(Math.random() * 1000));});const p3 = new Promise((resolve, reject) => {    setTimeout(() => resolve("C"), Math.floor(Math.random() * 1000));});

Promise.any() возвращает результат первого успешно завершившегося промиса среди p1, p2 и p3.

(async function() {    const result = await Promise.any([p1, p2, p3]);    console.log(result); // Печатает "A", "B" или "C"})();

Что, если ни один из промисов не завершится успешно? В таком случае Promise.any() сгенерирует исключение AggregateError. Нам нужно поймать это исключение и обработать.

const p = new Promise((resolve, reject) => reject());try {    (async function() {        const result = await Promise.any([p]);        console.log(result);    })();} catch(error) {    console.log(error.errors);}

В демонстрационных целях в Promise.any() передается только один промис. И этот промис завершается с ошибкой (reject). Приведенный выше код выведет следующую ошибку в консоли.

Оператор логического присваивания

Оператор логического присваивания объединяет логические операции (&&, || или ??) с присваиванием.

let x = 1; let y = 2;x &&= y; console.log(x); // 2

Строка 3 эквивалентна следующему выражению:

x && (x = y)

Или по-другому:

if(x) {    x = y}

Поскольку x - истинное значение, ему присваивается значение y, то есть 2.

Как и в случае с &&, мы можем поступить так же и с логическими операциями || и ??.

Оператор логического присваивания с ||

Пример кода.

let x = 1;let y = 2;x ||= y;console.log(x); // 1

Строка 3 эквивалентна следующему выражению:

x || (x = y)

Это означает, что операция присваивания происходит, только если x является ложным значением. В нашем коде x содержит 1, что является истинным значением, и, следовательно, присваивания не происходит. Вот почему наш код выводит 1 в консоли.

Оператор логического присваивания с ??

?? - это оператор нулевого присваивания (Nullish Coalescing operator) в JavaScript. Конкретно, при присвоении оператор проверяет, является ли левый операнд null или undefined и если это так, то вычисляет и присваивает правый операнд. Если же левый операнд не null и не undefined, то присваивается он.

let a;let b = a ?? 5;console.log(b); // 5

В строке 2, если значение a равно null или undefined, правая часть ?? вычисляется и присваивается переменной b.

Давайте теперь рассмотрим ?? вместе с =.

let x;let y = 2;x ??= y;console.log(x); // 2

Строка 2 в коде выше эквивалентна следующему выражению:

x = x ?? (x = y)

Здесь значение x равно undefined. Таким образом, выражение в правой части вычисляется и устанавливает x равным 2.

Подробнее..

Психбольница в руках пациентов, или Инфраструктура как продукт

08.04.2021 16:21:10 | Автор: admin

У бизнес-разработчиков за дедлайнами, сроками, клиентами и большими запусками может складываться впечатление, что инфраструктура выстраивает свой воздушный замок, который далек от того, что происходит в действительности. Захотев это изменить, Алексей Данилов из разработки перешел в команду инфраструктуры последние два года он развивает ее в Яндекс.Вертикали а это Яндекс.Работа, Яндекс.Недвижимость, auto.ru и Яндекс.Объявления.

Особенность Яндекс.Вертикалей ребята одновременно и бизнес-юнит Яндекса, и обычная IT-компания. Благодаря первому, у них есть возможность использовать яндексовую инфраструктуру, но, благодаря второму, разработчики смотрят на весь стек и используют то, что им удобно. Алексей сегодня поделится с нами, как это можно сделать более удобно и ближе к пользователям (в его случае к разработчикам). А также о том, почему на инфраструктуру сейчас надо смотреть как на продукт и какие вызовы это приносит команде, но что это дает в конечном итоге компании.

Infrastructure

Под инфраструктурой я подразумеваю всё, что окружает бизнесовый код и помогает ему от начала разработки фичи до момента ее доставки. Это и базы данных, и балансировка, и мониторинг, и CI с CD. Сюда же относятся особенности разработки/работы, библиотеки, тестирование, бизнес аналитика, бизнес-специфичные возможности и много чего еще. Это как завод, на котором вместо ручного труда автоматизированы все процессы, и результат мы получаем более быстрый и более качественный.:

При этом я рассматриваю инфраструктуру как единую платформу, а не набор разрозненных частей, которые я перечислил выше. И это уместно для компании любого размера. В облаках Amazon Web Services, Yandex Cloud автоматизация может строиться, например, на основе terraform. У вас может быть собственное железо или вы его где-то арендуете и на нем может быть развернут Kubernetes, Nomad, что-то еще. Команда тоже может быть любой от нескольких человек, которые в основном используют bash или terraform, и до сотен, со своими велосипедами.

И тогда возникает вопрос как добиться идеальной инфраструктуры Platform as a Service, который мы реализуем для наших пользователей, и вообще каковы критерии идеальности? Нам не нужно разрабатывать еще один Amazon или Kubernetes поэтому достаточно небольшой и простой системы, но у нас должна быть возможность ее расширения под наши use cases. Например, если у нас есть какие-то особенные АБ-тесты, особенные условия доставки (например, канареечный релиз ) и особенные правила безопасности это как раз то, что должна закрывать инфраструктура.

Ее основой, краеугольным камнем будут минимизация/ускорение разработки, упрощение поддержки и простота использования. Остальные требования понятность, доступность, стабильность и единообразность/распространение практик, а также скрытие низкоуровневых особенностей (чтобы никому не пришлось писать самому конфиг nginx или сложный Kubernetes манифест), техническая поддержка 24*7 и связанность компонентов конечно, тоже имеют место.

Если выполнить их все, ваша инфраструктура будет не только единой платформой или сервисом, но постепенно будет превращаться в продукт, как это случилось у нас.

Infrastructure/Platform as a product (PaaP)

Сначала мы, конечно, смотрели в сторону сторонних приложений. Например, мы серьезно рассматривали Spinnaker от Netflix. Но он написан на Java, а у нас все пишут на Go, и мы не хотели добавлять еще один язык. Во-вторых, он не поддерживает Nomad и Yandex.Cloud. Следовательно, нам пришлось бы его прилично дорабатывать и интегрировать с нашими внутренними особенностями, что практически равно форку. Поэтому мы стали писать свое.

Изначально мы задумывали Shiva как абстракцию над деплоем (в нашем случае над Nomad) и как multicloud. Но сейчас она уже приросла различными системами и интегрируется со всем, что у нас внутри есть:

Основная ее часть представлена в GitHub в виде карты сервисов. Она изменяется посредством пул-реквестов, конфигурирует балансировку, контролирует деплой и различные пайплайны доставки, SOX, PCI DSS и т.д. То есть это одно описание для полноценной работы:

Архитектура Shiva в упрощенном виде выглядит так:

Сервис постоянно развивается и сейчас он уже частично похож на продуктовый: есть БД, вокруг которой построены различные микро-сервисы, есть небольшие legacy-компоненты (компонент Updater и Shiva смотрят в одну базу) и т.д. То есть с технической точки зрения он развивается как обычный бизнесовый сервис.

UI мы реализовали в Telegram, а впоследствии написали WEBовский. Telegram-бот это CLI over Telegram все команды задаются в формате CLI, но дополнены различными контекстными действиями, кнопками и т.д. Нам нравится этот подход, потому что он кросс-платформенный, его легко обновлять и к нему всегда есть доступ. Можно спокойно обновить или откатить свой сервис в случае проблем. А также очень удобный механизм нотификации, когда вы получаете уведомление о начале процесса деплоя прямо в тележку.

И хоть WEB и получился удобным, все равно часть пользователей остается в Telegram. Потому что UX меняется в профессию приходит очень много молодых разработчиков. Мы привыкли к WEB, мы привыкли использовать Telegram-боты и вообще общаться в чате. С другой стороны, мы хоть и думаем сделать CLI с такой же функциональностью, но понимаем, что запросов на это не было.

Экономика

Когда встал вопрос об экономике, то первое, о чем мы подумали модная сейчас юнит-экономика: ее используют при создании стартапа, чтобы понять, насколько он экономически выгоден. К тому же это очень удобный фреймворк:

Но у инфраструктуры нет доходов, тогда как на этой схеме есть и доходы, и расходы. Доход от инфраструктуры равен 0 это расход компании, а ROI будет отрицательным: сколько инвестиций не вкладывай, они не отбиваются:

Profit = Revenue - Cost = 0 - X

Есть, конечно, плюшки в виде, например, бесконечного LTV (LifeTime Value) то есть наши клиенты от нас никогда не уйдут. Но в целом эта метрика нам не подходила, и мы стали думать в сторону velocity (скорость разработки), потому что ее довольно легко преобразовать в деньги. Например, если мы ускорили разработку на час в неделю, то это нетрудно соотнести со средней зарплатой у 500 разработчиков и получить какой-то видимый профит. Вообще инфраструктура это именно velocity, то есть скорость доставки ценности пользователю. Но у нас это не глобальная метрика, потому что velocity зависит от очень многих факторов. : Если смотреть сверху, то можно выделить:

  • code время написания самой логики ;

  • infrastructure code время написания инфраструктурного кода (код логов, метрик, алертов и т.д.)

  • environments время на настройку переменных окружения, секретов и т.д.;

  • delivery время, затраченное на доставку.

На последние три пункта инфраструктура влияет напрямую и может существенно упростить или ускорить их. Для примера рассмотрим механизм проверки новой конфигурации для сервиса. Сначала наша карта сервисов проверялась небольшим скриптом, и далее ее уже должен быть допроверить человек. Но там, где есть работа реального человека, Velocity стремится в 0, а скорость разработки сильно снижается. Поэтому, если у вас деплой или изменение конфигурации (базовые повседневные вещи) завязаны на тикеты или другие инструменты, где есть влияние ожидание работы человека это те точки роста, которые нужно сразу же автоматизировать.

В нашем случае мы написали GitHub-бота, который с очень большим спектром валидации проверял эту карту и мгновенно отвечал: либо всё хорошо, либо есть какие-то конфликты с другими картами и сервисами:

Следующим нашим пунктом разработки инфраструктуры как продукта стало внедрение user story.

User story

User story это фундамент любой продуктовой разработки и используется он в любых гибких методологиях идущих от Agile. Это не детальное описание задач давайте прикрутим здесь кнопку, которая будет делать вот это. User story это намерение пользователя: какую проблему пользователь решал, как он пытался её решать, почему он её решал, почему она возникла, и, наконец, как мы можем её решить. Разумеется, из user story выводится очень много различных решений. Но сам User story не является им это основа для решения и инструмент для выявления исходной проблемы.

В бизнесовой разработке user story пишется на повседневном и понятном пользователю языке. Для нас здесь была небольшая ловушка, потому что наши пользователи разработчики, они очень грамотные и могут принести любую историю вплоть до того, что надо внести изменения в БД. Поэтому мы должны были обязательно вытягивать основную проблематику находить инкремент ценной функциональности, реализуемой за короткий срок.

Приведу для примера две наши внутренние истории.

Пример 1. Залипает обработчик Кафки. Чинится только рестартом сервиса. Фикс в пути. Хочется иметь способ быстро перезапустить контейнеры.

Проблема была понятной и мы предложили сделать команду restart -l prod my_service, в которой указывается слой сервис, чтобы сервис рестартился через телеграм бота.

Пример 2. При обновлении конфигурации хочется перезапустить сервис без указания версии.

Решением стало: /run -l prod -v 1.0.7 my_service -> /run -l prod my_service.

Но изначально это выглядело как одна задача: сделайте нам такую команду для рестарта, после которого сервис притягивает новую конфигурацию. С точки зрения обычной таски это можно было сделать, а с точки зрения user story нельзя, потому что в первом примере люди видят проблему и периодически рестартят свой сервер, пытаясь найти, что же здесь не так. Если при этом приедет новая конфигурация, это принесет им куда больше проблем, то есть мы можем сделать хуже.

Поэтому первое, что мы сделали это идемпотентный рестарт, который ничего не подтягивал и при этом гарантировал, что конфигурация будет идентична. А вторым стал run, который можно указать без версии версии и он подтянет новую конфигурацию (новые переменные окружения, новые секреты и обновит остальное окружение). Более того, когда мы в дальнейшем их разделили, то user story с запуском из версии модифицировали и развили так, что run стал гарантировать изменение сервиса. А если ему нечего изменять и нечего подтягивать, то он об этом честно говорил.

То есть разделяя user story, мы можем их по фидбэку развивать дальше. Но даже просто использование user story дает огромный профит, так как мы начинаем слышать пользователя о его проблеме. Но если общаться с пользователями и вместе находить решение не каждая user story будет реализована, и не каждая так, как мы придумали.

Customer development (сustdev)

Инструмент сложный в продуктовой разработке, но в случае инфраструктуры он очень прост потому что мы разрабатываем новый продукт (фичу, решение или какую-то часть нашей системы) не сразу через код, а через пользователя. Можно даже провести ниндзя-сustdev, находясь в переговорке в ожидании какой-нибудь встречи или попивая кофеечек на кофе-поинт. Да и сам процесс не очень сложный:

  1. Выявляем потенциальную проблему (строим гипотезу), формируем идею или решение, но не внедряем ее, а идем к пользователям, чтобы поговорить с ними об их потребностях/болях/проблемах.

  2. Встречаемся с ними, например, на кофе-поинте или на рабочем месте. Можно пообщаться и в Зуме. И в обсуждении можно обнаружить, что проблема пользователям не важна она не так много времени занимает и не так сильно его напрягает, но зато у него есть более важные проблемы.

  3. Прорабатывая решение вместе, мы получаем либо изначальное успешное решение (если определили проблему верно) или более успешное (поняв из общения, что волнует пользователей) то есть в выигрыше все.

Например, у нас не было истории деплоев, и мы понимали, что нам она нужна. Мы напридумывали очень много сложных вещей, вплоть до того, чтобы в Telegram с помощью пейджинга показывать список последних 10 сервисов и уже думали, как же их спейджить туда-обратно и т.д. Но пообщавшись с разработчиками, мы поняли, как история начинается у большинства из них: они видят на графиках аномалию и м нужно посмотреть, не было ли деплоя не новая ли версия принесла эту аномалию?

Эта история решилась довольно просто. Мы добавили время деплоя в аннотации на Grafana, и теперь видя какую-то аномалию, можно понять, не было ли деплоя, в том числе деплоя зависимых сервисов. Эта история, кстати, ускорила у нас создание WEB UI.

А потом мы пообщались с тимлидами и выяснили, что они это видят совсем по-другому:

Developer: Я вижу по графикам проблемы нужно проверить, не связано ли это с выкаткой новой версии (см. график выше).

Team leads: Нужна возможность посмотреть кто, что, когда и зачем катил в прод:

Для тимлидов эту историю со списком, сложными фильтрами, поиском и т.д. мы реализовали намного позже. Потому что, во-первых, наш основной пользователь это обычный разработчик тот человек, который каждый день пишет код, делает фичи, раскатывает их, дежурит, заботится о сервисе и т.д. А во-вторых, это был уже этап, когда мы начали выделять и использовать портреты пользователя, а затем и портреты сервисов.

Портреты пользователей и сервисов

И то, и другое сложно реализуется в продукте, но легко и быстро в инфраструктуре. Портреты пользователей у нас были довольно простыми:

  • Разработчики: бэкенд, фронтенд и инфраструктура;

  • Team leads;

  • Тестировщики/Автотестировщики;

  • Аналитики;

  • Менеджеры, продукты, руководители.

По сути частичное отражение рабочих позиций :)

А портреты сервисов можно классифицировать по типу, языку или размеру. Первые бывают внутренними (realtime api, back api, async, cron, gateway, и т.д.), DB, внешними, saas и т.д. И они богатая точка роста, потому что это место отказа каких-то инфраструктурных частей. Это и наша точка роста, которую мы рассматриваем в будущем для себя.

Языки разработки мы рассматриваем нечасто, но иногда приходится смотреть: почему эта история возникает, у кого она возникает, потому что, например, у java и node.js бывают небольшие различия в работе, и это приходится учитывать.

По размеру портреты сервисов могут быть: function, microservice, service, monolith, distributed monolith и т.д. И если мы, например, понимаем, что проблематика идет из распределенного монолита, то отказываем в этой истории, потому что у нас хорошей практикой считаются мироксервисы и нашим PaaS мы популяризируем и упрощаем жизнь именно с ними..

Вот для примера портрет сервиса БД. С помощью этой карты ставятся бэкапы, происходит доступ к БД на чтение/запись и т.д. Плюс, сам сервис в свои зависимости прочерчивает эту карту, и мы знаем, что это за карта и от чего она зависит то есть, мы знаем, что сервис А зависит от базы данных В:

Feature request

Но одно дело найти проблему. Совершенно другое когда приносят уже готовое решение. Это самая богатая тема, которую можно использовать себе на благо.

Конечно, у нас есть проблемы:

  • Технически грамотный пользователь может принести уже готовое решение, например: Сделайте такой API, в БД все сохраните, и на этом хватит.

  • Личное знакомство потому что иногда мы отказываем в feature request или говорим, что будем делать по-другому, и возникает небольшой конфликт.

Но есть и решения:

  • Превращать в user story находить проблематику: узнавать, с какой проблемой столкнулся пользователь и как пытался решать;

  • Смотреть на доработку в контексте портретов кто к нам пришел, что за сервис, о чем идет речь;

  • Спорную доработку или ту, в которой мы не уверены, проверять через castdev, потому что все-таки feature request приносит один пользователь и непонятно, насколько это ценный функционал для всех;

  • Механизм голосований/рейтинга это самый мощный инструмент. Он не нов, используется во всех хороших продуктовых разработках, например, в терминале Тинькофф-Инвестиции. У них открытые feature request, где люди открыто голосуют и в итоге в работу идут те, что в топе:

Внутри это также можно легко реализовать, потому что мы работаем в рамках одного issue-трекера. Например, здесь фильтры по нашим feature request:

Из интересного: мы недавно этот вид сделали через наш WEB не таким формальным и увидели, что люди с большей охотой голосуют за идеи то есть это тоже важно:

Механизм feature request очень удобно использовать как часть синергии с пользователем и нам не нужно будет сильно прорабатывать такую историю ведь очень много пользователей проголосовали за нее. Мы можем видеть, кто голосует и это могут быть разные люди, которые поддерживают разные сервисы. Получается в итоге даже небольшой scoring функциональности мы точно понимаем и то, что она нужна, и зачем, и насколько это нужно пользователям. Голосование к тому же может заменить множество внутренних исследований таких, как сustdev.

Roadmap

Хочу отдельно сказать про Roadmap. Это кажется довольно банальной темой, но пообщавшись с разными компаниями, я обнаружил, что у команды инфраструктуры часто не бывает долгосрочного видения. Есть понимание, что они делают через неделю или месяц, но нет понимания, какой должна быть в итоге инфраструктура. И обычно это инфраструктура посредством одной кнопки, когда все описано в GitHub-репозитории и сделана она, например, на CI сборках. Куда стремиться непонятно.

Ценность даже не в самом плане, а в процессе планирования. Когда мы планируем, мы думаем, что хотим дать пользователям, поэтому roadmap удобно сделать открытым можно сразу получить фидбэк. Мы можем расставить истории по приоритетам, фидбэк может сказать нам, что большинству намного важнее то, что мы отложили. Так что публичные и регулярно обновляемые планы помогут вам скорректировать roadmap и строить долгосрочный бэклог на его основе.

Кстати, как получать обратную связь?

  • Тикеты и подписки на них. В нашем случае это самый рабочий инструмент, потому что мы работаем в одной компании, и в одном трекере.

  • Большие посты анонсы. Несколько раз в квартал мы делаем большие анонсы, где рассказываем про всю большую функциональность и опять же получаем фидбэк. Иногда мы получаем негативные отзывы, например: Зачем вы это делаете, если можно взять вот это и то. Тогда мы либо объясняем, либо понимаем, что, может и правда можно реализовать более просто.

  • У нас есть групповой чатик в Telegram/Slack/Microsoft Teams. В основном мы там собираем обратную связь, хотя он работает как инструмент технической поддержки, а также в нем выкатываем нововведения с небольшими инкрементальными релизами.

  • Открытые встречи для вопросов/ответов. Мы пока что провели её только один раз, но результат был неплохой.

  • Еще можно встречаться на демо продуктовой разработки и там отвечать на вопросы. Но мы не проводим демо сейчас для нас это большая потеря времени.

  • Еще можно использовать индекс потребительской лояльности NPS (Net Promoter Score). Это простые анкеты: насколько вы удовлетворены нашим сервисом, насколько вам удобно базовые вопросы для того, чтобы собрать общую статистику лояльности пользователей. Мы не используем NPS, потому что из чата получаем и критику, и позитив, а остальных записываем как нейтральных пользователей.

MVP

После планирования хочу напомнить про инструмент MVP (Minimum Viable Product). Это известный подход, но в инфраструктуре есть нюансы. Мы, как любой бизнесовый продукт, выкатываем частично недоделанный продукт, где-то не самый удобный, но минимально рабочий. Потому что мы не можем делать 2-3 месяца какую-то историю, выкатить её, получить средний фидбэк и потом еще месяц переделывать:

Henrik KnibergHenrik Kniberg

Поэтому мы стараемся поддерживать максимально инкрементальные релизы для того, чтобы потихонечку двигаться к тому функционалу, который запланировали в будущем и который, опять же, всем известен. А получая фидбэк, мы успеваем его корректировать:

Henrik KnibergHenrik Kniberg

Например, когда мы резолвили свой первый WEB, его функциональность была далека от функциональности Telegram, да и выглядел он неказисто, если честно. Но впоследствии он развивался, мы получали фидбэк, и в результате даже пошли по другому пути, потому что UXe (пользовательский опыт) отличается от того, что было в Telegram, и от того, что есть в WEB.

WEB сейчас поддерживает тот же самый функционал, но он в той или иной мере реализован в WEB немного по-разному. Плюс, он расширился: в нем есть истории деплоев, работа с картой и манифестом, с переменными окружения и т.д.

Overkill

Важно понимать, когда продуктовый подход становится слишком сложным. Бизнесовые команды становятся настолько большими, что состоят из множества людей с разной направленностью и спецификой. Есть product owner, цель и задачи которого выстраивать roadmap и определять, какие из историй делаются. Есть product менеджер, UX-специалисты, разработчики, тестировщики и т.д. Когда большие истории разбиваются только по user story и пытаются делать с добавлением какой-то пользовательской ценности. Всё это очень круто, но сейчас для нашей команды это overkill. Как в принципе, и A/B-тестирование.

Поэтому нужно четко определять, что мы используем, а что нет. Для небольших команд инфраструктуры, как наша, есть лайфхаки, которые помогают иметь результат не хуже:

  • Стараться понять: Зачем? То есть не Разработчик хочет 500 Тб БД, а: Разработчику нужно сохранять информацию о машинках (которую мы никогда не сохраняли) тогда мы сможем работать вместе над одной проблемой пользователя

  • Добавить в команду продуктового разработчика, потому что DevOps расширяется, кода мы пишем сейчас больше, и нужен разработчик, который пропитан этой атмосферой.

  • Добавить в команду фронтендера, понимающего в той или иной мере в интерфейсах. Да, это не профессиональный дизайнер, но для инфраструктуры этого достаточно.

  • Обязательно нужен механизм голосования за идеи для того, чтобы упростить scoring задач.

  • Собирать roadmap и сделать его открытым.

  • Периодически собирать аналитику не сложные и большие графики, но чтобы плюс-минус понимать, что происходит у пользователей.

  • Нужно много автотестов , так как я все еще не представляю вакансии тестировщика в команде инфраструктуры, как и дизайнера по крайней мере в 2021 году.

  • И основной пойнт больше синергии с пользователями. Потому что хорошую инфраструктуру ограниченной командой с ограниченными ресурсами можно делать только в синергии с пользователями вытаскивая самые ценные истории.

Итог

В заключении рекомендую книгу Психбольница в руках пациентов Алана Купера я ее прочитал очень давно и с огромным удовольствием, она меня вдохновила. Сейчас настало время, когда ее стоит рекомендовать не только product owner, продуктовым разработчикам, дизайнерам, но и команде инфраструктуры. Там очень много полезных примеров, в которых можно найти себя.

Подытоживая:

  • Разговаривайте со своими пользователями, находите проблематику надо искать проблемы, а не решения.

  • Относитесь к внутренней инфраструктуре как к единой платформе, как к единому PaaS.

  • Разрабатывайте PaaS как полноценный внутренний продукт.

  • Используйте проверенные простые продуктовые подходы, но только те, которые окажутся простыми. Если тот или иной подход (тот же сustdev или портреты пользователей) кажется сложным, возможно, сейчас их не стоит использовать.

  • Повышайте ценность PaaS (скорость/расходы разработки/поддержки).

  • Создавайте современные интерфейсы.

  • Примите, что современная команда инфраструктуры приближается к продуктовой. Её уже официально называют командой DevOpsеров. Эта мифологическая команда приближается к продуктовой по своим требованиям и тому, как она должна работать. Следование новым веяниям и использование современных подходов поможет создавать действительно удобную инфраструктуру

А любые сложности можно обойти в синергии с пользователем. Если вы создадите коммуникации и инструменты для этого, вы получите максимум фидбэка, а значит максимум информации, как инфраструктура может сделать компанию эффективней. Пишите, я готов к обсуждению.

Профессиональная конференция DevOpsConf 2021 пройдёт 31 мая и 1 июня этого года в Москве, в отеле Radisson Slavyanskaya. Расписание уже сформировано. На сайте вы можете познакомиться с программой и спикерами.

Как всегда, будут реальные кейсы компаний, сложные инженерные аспекты цифровой трансформации, многообразие технологий и подходов: наглядно, просто, с конкретными рекомендациями. Билеты здесь.

До встречи в оффлайне!

Подробнее..

Sibur Challenge 2020 или как мы фичи придумывали

21.12.2020 14:15:23 | Автор: admin

Всем привет! В этом году компания Sibur Digital вновь проводила крупный (по сравнению с другими российскими) чемпионат по анализу данных. Мы с другом в нём участвовали и хотели бы поделиться с читателями Хабра своим решением и опытом, полученным от участия. Конечно вряд ли мы америку откроем этой статьей, но какой-нибудь новичок в соревнованиях по АД точно сможет почерпнуть для себя что-то полезное.

Кто мы такие?

Мы студенты которые очень сильно увлеклись темой DS и ML. Впервые мы узнали об этой сфере на конференции AI Journey, проходившей в нашем вузе. С того момента прошли не один, и не два, и не три курса (от Омского Государственного Технического университета до Andrew NG) и теперь постоянно участвуем в хакатонах и соревнованиях(в некоторых даже заняли призовые места), параллельно ищем стажировку.

О задаче

Мы взялись за вторую задачу соревнования - "сопоставление названий".

Суть следующая : Сибур работает с огромным количеством новых компаний, и для оптимизации рабочего процесса им было бы полезно понимать, что они работают с уже ранее знакомым холдингом. К примеру "Сибур Нефтехим" и "СИБУР ИТ" из одного холдинга, и при работе с одной из этих компаний было бы полезно использовать накопленную ранее информацию о холдинге СИБУР.

Перефразируем задачу на язык DS. Даны два названия, по ним мы должны определить - принадлежат ли компании одному холдингу или нет.

name_1

name_2

is_duplicate

Japan Synthetic Rubber Co

Jsr Bst Elastomer

1

JSR Corporation

BST ELASTOMERS CO.

0

Примерно так выглядел датасет.

Предобработка данных

В первую очередь мы привели данные к латинице с помощью волшебного модуля unidecode. Далее привели к нижнему регистру, убрали всякий мусор в виде ненужных знаков препинания, двойных пробелов и т.д.

from unidecode import unidecodeimport redef preprocess(text: str):    text = unidecode(text)    text = text.lower()    text = re.sub(r'[\.,]+', '', text)    text = re.sub(r"\(.*\)", ' ', text)    text = re.sub(r"[^\w\s]", ' ', text)    text = re.sub(r'\b\w\b', ' ', text)    text = ' '.join(text.split())    return text

После взялись за удаление ненужных слов. Первыми в список мусора полетели названия стран в названиях, для этого взяли модуль pycountry(который любезно подсказали организаторы в бейзлайн решении) и немного дополнили этот список стран их сокращениями, аббревиатурами которые сами нашли в выборке.

Со стоп словами было посложнее. Конечно мы сразу удалили некоторое количество самых частотных слов, но понижая порог количества вхождений для удаления, мы заметили, что большинство названий в выборке просто остается пустым. При этом в "уцелевших названиях" остаются такие слова как "shanghai", и, очевидно они никак не подчеркивают уникальность компании, а лишь увеличивают путаницу среди шанхайских компаний. В результате пришлось из наиболее частотных слов самим выбирать бессмысленные и удалять их.

Поиск фичей

Взявшись за соревнование мы сразу решили, что не будем заострять свое внимание на сложных ансамблях моделей и огромных нейронных сетях, а постараемся подойти креативнее и больше работать с данными - искать закономерности и на их основе придумывать фичи (как минимум, второй подход казался нам поинтереснее).

В целом, можно обобщить : мы пытались использовать придуманные фичи для оценивания "похожести" строк, и уже на них обучать модель.

Первый прорыв нам дал признак "сколько букв сначала совпадает в двух словах подряд". Обучив логистическую регрессию в начале соревнования только на одном этом признаке мы получили результат чуть более 0.3 и перепрыгнули большинство в начале соревнования. В дальнейшем мы использовали не просто число совпадающих букв, а число совпадающих букв делим на суммарное количество букв обоих названий.

Вторым полезным признаком оказался коэффициент Жаккара взятый по словам из двух названий. Т.е мы разделили число пересекающихся слов в названиях на количество уникальных слов в двух названиях.

И в конце мы добавили бинарный признак, который непонятно почему пришел нам в голову последним, хотя на первый взгляд он самый очевидный. Содержит ли одно название все слова другого.

Остальные признаки, которые не оказались столь удачными, приведены в списке ниже:

  • количество совпадающих первых гласных, согласных

  • количество совпадающих первых буквы сокращения (аббревиатуры)

  • стандартные метрики: левенштейн, яро винклер, фузи вузи

  • tfidf - при подсчёте жаккара (или косинусного расстояния с нграммами)

  • сортировать уникальные слова в имени и считать наши метрики

  • процент пересекающихся ngram

  • количество совпадающих первых букв каждого слова (брать максимум)

  • количество букв первого, количество букв второго

  • количество слов первого, количество второго

Модель и блендинг решений

Как упоминалось выше, мы не собирались строить свое решение целиком и полностью на сильной модели, поэтому в качестве классификатора использовали XGBoost, который показал себя получше других простых моделей. И мы получили результат ~ 0.59 на лидерборде.

Далее мы понимали, что было бы неплохо обменяться с кем-то идеями и объединить решения. Мы познакомились с двумя другими участниками(Алехандро, Дмитрий, привет!), и заблендили наши решения, получив скор 0.69 на лидерборде. Так получилось, что все три наших решения имели разные подходы к задаче, поэтому их объединение и улучшило результат.

Выводы

В любой истории главное - выводы, поэтому, уверен, это будет самая интересная часть.

Начать стоит с того, что помогло нам достичь результата. В первую очередь это то, что мы не пошли простым путем fit_predict, а постарались посмотреть на задачу с разных сторон. Перепробовали огромное количество различных методов(начиная от метрики Левенштейна и подсчитывания косинусного расстояния и заканчивая сиамскими нейросетями). Провели анализ ошибок модели, который помог выстроить правильную предобработку и обновить словарь стоп-слов.

Что можно было доработать?

Можно было учитывать семантику слов или выдавать словам веса: если слово в двух названиях совпало и оно полезно (относится к названию компании) - имеет вес, мы автоматически считаем что оно настолько же вредно в "разнице" слов; использование как можно больше внешних данных с названием компаний и т. д. Также не забывать анализировать наблюдения, на которых ошибается модель (False Positive, False Negative), и на основе этого конструировать новые признаки.

P.S.

Весь код лежит здесь

Если хотите связаться с нами : matnik2001@gmail.com , domonion@list.ru

Подробнее..

Перевод Вводная статья по реализации целе-вероятностного кодирования переменных (Feature Target Encoding)

28.02.2021 22:22:33 | Автор: admin

Недавно я сделал проект, в котором целевая переменная была мультиклассовой, поэтому, я искал подходящие пути для кодирования категориальных признаков. Я нашёл множество статей, перечислявших преимущества кодирования через среднее значение целевой переменной перед другими методами, а также то, как выполнить эту задачу в двух строчках кода, используя библиотекуcategory_encoders.Однако, к своему удивлению, я обнаружил, что ни одна статья не продемонстрировала этого метода для мультиклассовой целевой переменной.Я просмотрел документацию category_encoders, и понял, что библиотека работает только для бинарных или вещественных переменных, посмотрел оригинальную работу Даниэля Мисси-Баррека (Daniele Micci-Barreca), который ввел средне-целевую кодировку (mean target encoding) и так же не обнаружил ничего толкового.

В этой статье я дам обзор документа, в котором описана кодировка по целевому признаку, и покажу на примере, как целевая кодировка работает для двоичных проблем.

Теория

Итак: если вас спрашивают 0 / 1, кликнули / не кликнули или кошка / собака, то ваша проблема классификации бинарная;если вы хотите ответить красный либо зеленый или синий, но может быть желтый или седан против хэчбэка и все против внедорожника, тогда проблема в нескольких классах.

Вот что вкратце говорится в упомянутой выше статье о категориальной цели:

Результат любого наблюдения можно отобразить через оценку вероятности целевой переменной.

При классификации, числовое представление будет соответствовать вероятности выпадения целевой переменной, обусловленной значением категориальной переменной.

Это эффективнее простого числового кодирования.

Для избежания переобучения при небольшом количестве наблюдений, может применяться сглаживание средних значений.

Давайте посмотрим на это практически.В бинарной задаче целью является либо 0, либо 1. В таком случае оценка вероятности для категориальной переменной может быть задана эмпирической бейесовской вероятностью P (Y = 1 | X = Xi), т.е.:

n(Y) - общее количество строк с 1 в целевой метрике,

n(i) - количество строк с i-той категории,

n(iY) - количество строк с 1 в целевой метрике в i-той категории.

Следовательно, дробь в левой части представляет собой вероятность наличия 1 в i-той категории, а дробь в правой - вероятность наличия 1 в общих данных. - это функция, добавляющая монотонный вес от 0 до 1, возрастающий с увеличением n(i), который помогает при небольшом количестве наблюдений в категориях.

Если вы использовали TargetEncoder из библиотеки category_encoders, k - это параметр min_sample_leaf, а f - параметр сглаживания.Если вы использовали TargetEncoder из библиотеки category_encoders, k - это параметр min_sample_leaf, а f - параметр сглаживания.

Если вы использовали TargetEncoder из библиотеки category_encoders, k - это параметр min_sample_leaf, а f - параметр сглаживания.

Введение весового коэффициента имеет смысл, когда размер выборки большой, и мы должны уделять больше внимания оценке вероятности, определяемой в левой части выражения. Однако, если размер выборки невелик, то мы должны заменить оценку вероятности нулевой гипотезой, заданной первоначальной вероятностью зависимого атрибута (т. е. средним значением от всех Y). С помощью такого преобразования пропущенные значения обрабатываются подобно остальным переменным.

В документе та же концепция распространяется и на мультиклассовые целевые признаки.У нас есть по одной новой функции для каждого целевого класса.Таким образом, каждая функция фактически является числовым представлением ожидаемого значения ровно одного целевого класса с учетом значения категориального признака.Конечно, количество входов может значительно увеличиться, если цель имеет очень большое количество классов, однако на практике количество значений обычно невелико.Подробнее об этом будет другая статья.

А теперь, давайте рассмотрим ситуацию с двоичной классификацией на примере.

Практика

Давайте посмотрим на пример двоичной целевой переменной.

Какова вероятность того, что целевая переменная примет значение 1, если значение признака пол Мужской?

Посчитаем: 1/2 = 0,5.

Аналогичным образом, какова вероятность того, что Target будет 1, если пол Женский?

Посчитаем: 1/4 = 0,25.

Достаточно просто?

Теперь, если вы замените все значения Female на 0,25, вы рискуете так называемым переобучением.Это происходит потому, что общая вероятность единицы равна 4/9 = 0,4.

Чтобы учесть этот факт, мы добавляем вес к этой предварительной информации, используя формулу, из предыдущего раздела.

Установка min_sample_leaf, k = 1 и сглаживание, f = 1,

У нас есть две строки со значением Male, поэтому n = 2;

(Male)=1/(1+exp(-(21)/1))=0.73 # Weight Factor for 'Male'Target Statistic=(Weight Factor * Probability of 1 for Males) + ((1-Weight Factor) * Probability of 1 Overall)S(Male)= (0.73 * 0.5) + ((10.73) * 0.4) = 0.485

Аналогично, для Female у нас четыре строки, поэтому n = 4;

(Female)=1/(1+exp(-(41)/1))=0.95 #Weight Factor for 'Female'Target Statistic=(Weight Factor * Probability of 1 for Females) + ((1-Weight Factor) * Probability of 1 Overall)S(Female)= (0.95 * 0.25) + ((10.95) * 0.4) = 0.259

Если приглядеться, можно заметить, что увеличивает значимость тех категорий, для которых количество строк больше.В нашем примере было 4 строки с полем Female, и только 2 строки с полем Male.Соответствующие весовые коэффициенты составили 0,95 и 0,73.

Таким образом, мы заменим все вхождения Male на 0,485 , а Female на 0,259.Аналогичным образом можно рассчитать значение Другое.

Поздравляю!Вы только что реализовали вероятностную кодировку через целевой признак!

Не верите?

Убедитесь сами, запустив этот код, который делает то же самое с использованием библиотеки category_encoders:

!pip install category_encodersimport category_encoders as ce x=['Male','Male','Female','Female','Female','Female','Other','Other','Other']y=[1,0,0,0,0,1,1,1,0]print(ce.TargetEncoder().fit_transform(x,y))

Вывод

В этой статье я описал недостаток класса TargetEncoder библиотеки category_encoders.Я объяснил и резюмировал статью, в которой было введено целевое кодирование.Я объяснил на примере, как категории можно заменить числовыми значениями.

Следующая статья будет полностью посвящена тому, как мы можем сделать то же самое для мультиклассовых целевых признаков.

Будьте на связи!

Подробнее..

Перевод Мульти-классовое целе-вероятностное кодирование (Multi-Class Target Encoding)

04.03.2021 00:14:15 | Автор: admin

Что не так с TargetEncoder из библиотеки category_encoders?

Эта статья является продолжением предыдущей статьи, в которой объяснялось, как на самом деле работает целе-вероятностное кодирование. В этой статье мы посмотрим в каких случаях стандартное решение библиотеки category_encoders дает неверный результат, а кроме того, изучим теорию и пример кода для корректного мульти-классового целе-вероятностного кодирования. Поехали!

1. Когда ошибается TargetEncoder?

Посмотрите на эти данные. Цвет - это особенность, а цель - это цель. Наша цель - кодировать цвет на основе Target.

Давайте сделаем для этого обычную целе-вероятностную кодировку.

import category_encoders as cece.TargetEncoder(smoothing=0).fit_transform(df.Color,df.Target)

Хмм выглядит не очень, не так ли? Все цвета были заменены на 1. Почему? Так происходит потому, что TargetEncoder принимает среднее значение всех целевых значений для каждого цвета, а не вероятность.

Хотя TargetEncoder корректно работает в случае, когда у вас есть двоичная цель, имеющая 0 и 1, он будет давать сбой в двух случаях:

  • Когда цель двоичная, но не 0/1 (хотя бы, например 1 и 2).

  • Когда цель - мультикласс, как в приведенном выше примере.

Так что же делать?!


Теория

В оригинальном документе Daniele Micci-Barreca, который вводит средне-целевую кодировку говориться про мульти-классовые целевые переменные.

Допустим, мы имеем n классов. Теория гласит, что первым делом нужно закодировать значения класса в бинарные переменные. Это даст n двоичных столбцов, по одному на каждый класс цели. Однако только n-1 двоичных столбцов будут линейно независимы, так что в принципе, любой из них можно отбросить. После чего применим обычное целе-вероятностное кодирование для наших новых категорий, используя каждую из двоичных меток, по одной за раз.

Давайте разберемся на примере.

Пример

Продолжим с предыдущими данными.

Шаг 1: Бинарное кодирование мульти-классовой категории.

enc=ce.OneHotEncoder().fit(df.Target.astype(str))y_onehot=enc.transform(df.Target.astype(str))y_onehot

Обратите внимание, что столбец Target_1 показывает наличие либо отсутствие значения 0 в исходном столбце Target. Он принимает значение 1 если в Target есть 0, либо 0 в противном случае. Точно так же столбец Target_2 показывает наличие или отсутствие 1 в Target.

Шаг 2: Кодируем цвет, используя каждую из бинарных категорий.

class_names = y_onehot.columnsfor class_ in class_names:     enc = ce.TargetEncoder(smoothing = 0)    print(enc.fit_transform(X,y_onehot[class_]))

Для класса 0

Для класса 1

Для класса 2

Шаг 3: Если есть другие категории, кроме цвета, повторяем шаги 1 и 2 для них.

Готово!

Таким образом, на выходе получаем такой набор данных:

Обратите внимание, что для ясности я закодировал здесь все три столбца Color_Target. Если вы знаете бинарное кодирование, то слышали, что один из столбцов можно удалить без потери информации. Следовательно, здесь мы можем безопасно удалить, например, столбец Color_Target_3 (либо какой-то другой) без потери информации.

Практика

Вы здесь за кодом, не так ли?!

Ниже представлена функция, которая принимает на вход таблицу данных и объект целевой метки типа Series. Функция df может иметь как числовые, так и категориальные переменные.

def target_encode_multiclass(X,y): #X,y are pandas df and series    y=y.astype(str)  #convert to string to onehot encode    enc=ce.OneHotEncoder().fit(y)    y_onehot=enc.transform(y)     class_names=y_onehot.columns  #names of onehot encoded columns    X_obj=X.select_dtypes('object') #separate categorical columns    X=X.select_dtypes(exclude='object')    for class_ in class_names:        enc=ce.TargetEncoder()        enc.fit(X_obj,y_onehot[class_]) #convert all categorical        temp=enc.transform(X_obj)       #columns for class_        temp.columns=[str(x)+'_'+str(class_) for x in temp.columns]        X=pd.concat([X,temp],axis=1)    #add to original dataset    return X

Резюме

В этой статье я показал, что не так с TargetEncoder из библиотеки category_encoder, объяснил, что говорится в оригинальной статье о целевом кодировании мультиклассовых переменных, продемонстрировал всё это на примере и предоставил рабочий модульный код, который вы можете подключить к своему приложению.

Подробнее..

MVP на примере швейцарского ножа

04.06.2021 14:18:19 | Автор: admin

MVP (minimum viable product) - это первая версия вашего продукта, с помощью которой вы, как создатель продукта:

  • подтверждаете гипотезу о необходимости конкретного решения, опираясь на поведение пользователей;

  • собираете обратную связь от ваших будущих пользователей;

  • пытаетесь продать (или уже продаёте) ваше решение пользователям.

Пройдёмся по этим пунктам.

Возьмём в качестве примера не сложный IT-продукт, а самый обычный, бытовой предмет: швейцарский нож.

Однажды люди нащупали гипотезу, которая могла явиться проблемой: что если я оказываюсь в диком лесу, и мне нужно срезать гриб и отметить это, выпив бутылку шампанского? Носить с собой штопор и нож - попросту неудобно (да и кто в здравом уме берёт с собой в лес штопор?). Что, если объединить эти инструменты в одно?

Эти люди (назовём их фаундерами) попробовали совместить штопор и нож в одном инструменте, создали первый прототип (получилось криво и неудобно, но годно), и попытались продать будущим клиентам.

Представим, что у них это получилось. Что им делать в таком случае?

  1. продолжать продавать текущее решение, параллельно добавляя туда новые инструменты (функции), опираясь на обратную связь и пожелания покупателей;

  2. совершенствовать текущее решение, делать его более удобным (работать над UX);

  3. собирать обратную связь от пользователей и корректировать продукт.

Но всё получается с первого раза только у создателей швейцарских ножей. В реальной жизни 42% стартапов умирают от отсутствия спроса. Каждый раз фаундеры могут заходить на рынок с новым набором инструментов (пробовать разные гипотезы), либо, если всё идет наперекосяк - менять бизнес модель (сделать Pivot).

С каждой версией швейцарского ножа, нашим фаундерам нужно как можно быстрее выпустить образец на рынок, чтобы услышать обратную связь от покупателей.

Как это можно сделать?

  1. Производить и пытаться продать совсем маленькие партии;

  2. Экономить на качестве (но не так, чтобы получилось совсем плохо).

Именно такой подход: быстро создать решение, протестировать на пользователях, услышать обратную связь, и выйти на второй цикл улучшения продукта (или на смену бизнес-модели) и можно назвать MVP-подходом.

Как отбирать гипотезы и функции

Гипотеза - это предположение о проблеме клиента.

В одной версии продукта стоит тестировать только одну гипотезу за раз. При этом гипотеза должна быть смелая. Хотят ли люди есть грибы? Определенно да! Готовы ли пить шампанское? О да! Но может ли кому-то внезапно понадобится и нож, и штопор одновременно? Это уже гипотеза, которую стоит протестировать!

Теперь перейдём к тому, что можно включить в первую версию продукта. Ответ прост: нужно включать те функции, которые:

1) работают на проверку ключевой гипотезы. Не добавлять в швейцарский нож сам нож - кощунство;

2) дают наибольшую ценность для пользователя. Да, в швейцарский нож можно напихать ещё и трекер шагов, и bluetooth колонку, но основную ценность в нашем примере имеет нож и штопор.

Все функции, которые не удовлетворяют этим требованиям, можно смело оставлять на будущее.

Валидация без продукта

Можно ли подтвердить гипотезу относительно продукта, совсем не создавая никакого решения?

В IT-сфере - можно, если быть чуть хитрее и умнее, чем наши фаундеры из примера.

К примеру, они могли создать лендинг Чудо Швейцарские Ножи, повести на него трафик из социальных сетей, и посмотреть, оформляют ли люди предзаказ на этот товар. Если да - то его можно производить.

Примеров таких предзапусков в истории множество, но вот мой любимый:

Около 2-х лет создатели таск-менеджера Sunsama запустили на главной странице анкету, в которой опрашивали рабочие привычки будущих пользователей (сколько человек в команде, каким таск-менеджером пользуются сейчас, где хранят задачи и т.д.). Когда вышла первая версия продукта, доступ получили только те, кто подошёл в качестве целевой аудитории этого продукта. Таким образом создатели Sunsama убили трёх зайцев одним махом: исследовали аудиторию, получили первых последователей и подогрели интересы прочих, кто доступ не получил.

К слову, я даже пытался подобрать правильные ответы к анкете, чтобы получить доступ. И у меня это получилось! Но то, что оказалось внутри, мне уже было не интересно (не зря же я подбирал ответы).

Продавать или нет?

Автор книги Бизнес с нуля. Метод Lean Startup для быстрого тестирования идей и выбора бизнес-модели Эрик Рис говорит, что MVP с первого дня должен продавать. И я с ним абсолютно согласен.

Мы создаем продукты не только для того, чтобы решать проблемы и делать мир лучше, но и чтобы зарабатывать деньги. Если у вас есть инвестиции и собственные запасы, чтобы оплатить работу дизайнеров и разработчиков на долгие годы вперёд (чтобы впоследствии монетизировать платформу), это прекрасно. Если нет продукт должен начать приносить вам деньги уже сегодня (за счёт подписки на сервис, рекламы или взимания платы за интеграцию).

Ошибки при разработке MVP

MVP - это непаханное поле для больших ошибок и слитых бюджетов!

Вот какие ошибки вы можете совершить по ходу пьесы:

  • Включать слишком много необязательных функций. Представьте, что вы Роден (со швейцарским ножом в руке), и без сожаления выбрасывайте то, что не даёт ценности вашему продукту и не поддерживает его жизнеспособность;

  • Не слушать обратную связь от пользователей. Если вы не проводите тестирования и не слушаете обратную связь от пользователей, то вообще непонятно, зачем вы затеяли MVP. Возможно, вам стоит сразу пилить полноценный продукт и оказаться без миллиона в кармане?

  • Слишком стараться. Есть выражение, что если вы показываете первую версию продукта, и вам не стыдно - то вы уже перестарались. Поверьте, у вас будет бесконечно много шансов улучшить ваш продукт, и делать сразу идеально вовсе не обязательно!

  • Недостаточно стараться. Можно придумать отличную идею совместить нож со штопором, но если сделать это в стиле и таааак сойдёт!, то вы рискуете, что ваш пользователь окажется без руки, пользуясь вашим чудом техники. Перефразирую: в 21 веке уже недостаточно сделать косое и кривое приложение. Аппетиты и желания клиентов растут, поэтому постарайтесь, чтобы ваша первая версия выглядела хотя бы симпатично.

  • Влюбиться в продукт. Возможно, посреди разработки вам придется сменить концепцию. Возможно, вам придётся пожертвовать функциями, которые вам были так близки! А возможно, и весь продукт придётся прикрыть. Не влюбляйтесь в ваш продукт, иначе любые изменения будут даваться с трудом. Держите голову холодной, будьте гибкими, слушайте ваших пользователей. Помните, что подкидывать вам проблемы и задачи - это их основная работа.

Подробнее..

Категории

Последние комментарии

  • Имя: Макс
    24.08.2022 | 11:28
    Я разраб в IT компании, работаю на арбитражную команду. Мы работаем с приламы и сайтами, при работе замечаются постоянные баны и лаги. Пацаны посоветовали сервис по анализу исходного кода,https://app Подробнее..
  • Имя: 9055410337
    20.08.2022 | 17:41
    поможем пишите в телеграм Подробнее..
  • Имя: sabbat
    17.08.2022 | 20:42
    Охренеть.. это просто шикарная статья, феноменально круто. Большое спасибо за разбор! Надеюсь как-нибудь с тобой связаться для обсуждений чего-либо) Подробнее..
  • Имя: Мария
    09.08.2022 | 14:44
    Добрый день. Если обладаете такой информацией, то подскажите, пожалуйста, где можно найти много-много материала по Yggdrasil и его уязвимостях для написания диплома? Благодарю. Подробнее..
© 2006-2024, personeltest.ru