Quantcast
Channel: Категорія [Статьи] — DOU
Viewing all 2421 articles
Browse latest View live

Обзор С++ фреймворков для внедрения зависимостей: kangaru и [Boost].DI

$
0
0

Добрый день, уважаемые читатели! Меня зовут Кирилл Пшеничный. Я разработчик C++ в TeamDev. Основной моей задачей является разработка С++ библиотеки для интеграции с open-source проектом. В ходе данного процесса используются различные подходы: от использования функций обратного вызова (callbacks) до межпроцессорного взаимодействия (IPC). В данной статье я хотел бы рассказать о парадигме под названием Dependency Injection, которая призвана, в частности, упрощать взаимодействие и связывание различных частей системы.

Подход Dependency Injection позволяет сделать архитектуру бизнес-приложения гибкой и расширяемой. Этот подход широко известен, и каждый язык имеет множество фреймворков для реализации такого архитектурного решения. Например, Dagger и соответствующие компоненты во фреймворке Spring для Java, Microsoft Unity Framework для C#, Python Inject для Python. Язык С++ не является исключением и имеет ряд библиотек для внедрения зависимостей.

В этой статье я расскажу о специфике использования двух DI-фреймворков — kangaru и [Boost].DI — на примере несложной системы банковского учета.

Семантика Inversion of Control, Dependency Injection, Dependency Container

Основная идея инверсии управления (Inversion of Control, далее — IoC) заключается в том, что логику связывания и вызова различных компонентов системы программист вызывает не напрямую, а посредством использования IoC-контейнера. К такой логике, например, относятся создание объектов, логирование, кеширование, обработка исключений и вызовы доменных операций. В отличие от классического подхода, в котором программист полностью контролирует все вызовы методов и функций, IoC позволяет делегировать выполнение части бизнес-логики third-party-фреймворку.

Подход преследует такие цели:

  • увеличение гибкости системы;
  • повышение атомарности модулей и сущностей;
  • упрощение дальнейшего процесса замены отдельных модулей.

Внедрение зависимостей (Dependency injection, далее — DI) является одним из способов реализации IoC-подхода. Основной принцип DI — выстраивание отношения client — service. Client — это определенный компонент системы (метод, модуль, сущность), которому для реализации логики нужен сторонний компонент — service. При этом client не ищет и не создает необходимый компонент, а получает его извне, из контейнера зависимостей (Dependency Container, далее — DC).

Ниже приведен простой пример двух сущностей. Clientзавязан на использование определенного функционала Service. Для решения задачи Clientсам создает необходимый объект и полностью управляет его жизненным циклом:

struct Service {
       void doSmth() {
       }
};

class Client {
      
 Client() : service_(std::make_unique<Service>()) {}     
 void delegate() {
     service_->doSmth();
 } 

 std::unique_ptr<Service> service_;

};

Тот же код с использованием DI:

struct Service {
       void doSmth() {
       }
};

class Client {

 Client(Service* service) : service_(service) {}

 void delegate() {}

 Service* service_;

};

Отличие между этими двумя подходами заключается в следующем:

  • Client не создает Service, а получает его извне;
  • Client не контролирует жизненный цикл Service, а только использует его API.

Какие преимущества дает этот подход?

  • уменьшение количества boilerplate code, связанного с конструированием объектов;
  • упрощение процесса тестирования путем замены реальных объектов (драйверов БД, сетевых соединений) «заглушками» (stubs) и mock-объектами;
  • возможность независимой и параллельной разработки разных компонентов системы — достаточно знать только интерфейсы;
  • реализация различных сервисов в зависимости от конфигураций.

Демонстрационный проект

Для практической демонстрации возможностей рассматриваемых фреймворков была разработана несложная программа, моделирующая банковскую систему. Исходный код находится здесь. Для сборки вам понадобится библиотека boost и компилятор C++ с поддержкой стандарта С++14. В репозитории две ветки: master содержит реализацию DI с помощью kangaru, di_dependency использует [Boost].DI.

Система поддерживает три типа банковских депозитов:

  • SavingsDeposit — депозит с низкой процентной ставкой и постоянным обязательным наличием минимальной суммы на счете;
  • FixedDeposit — депозит, процентная ставка которого растет с течением времени; для него запрещены операции пополнения и снятия средств;
  • CurrentDeposit — депозит с нулевой процентной ставкой и овердрафт-лимитом.

Клиенты не взаимодействуют с моделью напрямую, а «общаются» с ней посредством сервисов AccountServiceи DepositService. Взаимодействие со слоем данных происходит через соответствующие интерфейсы репозиториев — AccountRepositoryи DepositRepository.

И здесь в процесс включается DI. Работая с абстракциями, мы в любой момент можем заменить имплементацию того или иного компонента. Например, во время тестирования можно использовать репозиторий, который управляет предопределенным набором данных, в продакшене — репозиторий для управления реальной базой данных; можно использовать локальную реализацию сервисов при тестировании, gRPC — в продакшене.

На UML-диаграмме показаны сервисы и их зависимости.

kangaru DI Framework

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

Объекты, которыми управляет kangaru, называются сервисами. Они представляют собой обертки для типов, которые участвуют в процессе DI. Такими типами могут быть конкретные классы, абстракции, интерфейсы.

Для применения пользовательских структур в дальнейшем процессе DI необходимо для каждой структуры создать класс сервиса и наследовать его от библиотечного шаблона kgr::service<>. На практике это выглядит следующим образом:

struct Delegate {
       void sayHello() {
           std::cout << "Hello\n";
       }
};


class Client {
   public:
       explicit Client(Delegate delegate) : delegate_(delegate) {}
       void hello() {
           delegate_.sayHello();
       }

   private:
       Delegate delegate_;

};

struct DelegateService : kgr::service<Delegate> {};
struct ClientService : kgr::service<Client, kgr::dependency<DelegateService>> {};

Здесь у нас два сервиса: DelegateServiceи ClientService. Зависимость между сущностями в модели представлена в сервисе с помощью шаблонного класса kgr::dependency. В качестве параметров шаблона следует передавать сервисы, которые будут создавать нужные объекты.

Создание нужных объектов происходит в объекте kgr::container:

int main() {
   kgr::container container;
   Client client = container.service<ClientService>();
   client.hello();
   return 0;
}

По умолчанию kgr::serviceсоздает новый объект каждый раз при запросе. Если вам нужны глобальные объекты, используйте kgr::single_service.

Вернемся к нашей банковской системе. Как было сказано ранее, нам хотелось бы иметь множество имплементаций, которые мы могли бы использовать в разных конфигурациях. Для декларации сервисов полиморфных объектов kangaru предлагает механизм полиморфных сервисов. Наследование от типа kgr::polymorphicпри декларации сервиса сообщает фреймворку, что в дальнейшем такой сервис может быть замещен:

struct DelegateService : kgr::service<Delegate, kgr::polymorphic> {};

struct Delegate {
       virtual ~Delegate() = default;
       virtual void sayHello() {
           std::cout << "Hello\n";
       }
};

struct PoliteDelegate : public Delegate {
   void sayHello() override {
       std::cout << "Hello, ladies and gentlemen\n";
   }
};

class Client {
   public:
       explicit Client(Delegate& delegate) : delegate_(delegate) {}
       void hello() {
           delegate_.sayHello();
       }

   private:
       Delegate& delegate_;

};

struct DelegateService : kgr::single_service<Delegate>, kgr::polymorphic {};
struct PoliteDelegateService :
 kgr::single_service<PoliteDelegate>, 
 kgr::overrides<DelegateService> {};

struct ClientService : kgr::service<Client, kgr::dependency<DelegateService>> {};

Оверрайд сервиса происходит путем наследования от шаблонного типа kgr::overrides. В качестве аргумента шаблона передаем родительский сервис.

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

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

Имплементации Bootstrapperзаполнят контейнер зависимостей конкретными реализациями сервисов:

struct AccountRepositoryService : kgr::abstract_service<Repository::AccountRepository> {};
struct DepositRepositoryService : kgr::abstract_service<Repository::DepositRepository> {};

struct AccountServiceService : kgr::abstract_service<Service::AccountService> {};
struct DepositServiceService : kgr::abstract_service<Service::DepositService> {};

class Bootstrapper : public boost::noncopyable {
   public:
       virtual ~Bootstrapper() = default;

       Service::DepositService& getDepositService() {
           return container_.service<DepositServiceService>();
       }

       Service::AccountService& getAccountService() {
           return container_.service<AccountServiceService>();
       }

   private:
       virtual void initRepos() = 0;

       virtual void initServices() = 0;

   protected:
       kgr::container container_;
};

Класс TestContainerBootstrapperпредставляет тестовую конфигурацию системы. В качестве репозиториев в такой конфигурации используется локальный набор данных, в качестве сервисов — локальные имплементации.

Декларируем конкретные сервисы с определенными имплементациями:

struct LocalAccountRepositoryService :
       kgr::single_service<Repository::LocalRepo::AccountRepositoryImpl>,
       kgr::overrides<DP::AccountRepositoryService> {};

struct LocalDepositRepositoryService :
       kgr::single_service<Repository::LocalRepo::DepositRepositoryImpl>,
       kgr::overrides<DP::DepositRepositoryService> {};

struct LocalAccountServiceService :
       kgr::single_service<Service::Impl::AccountServiceImpl, kgr::dependency<DP::AccountRepositoryService>>,
       kgr::overrides<DP::AccountServiceService> {};

struct LocalDepositServiceService :
       kgr::single_service<Service::Impl::DepositServiceImpl, kgr::dependency<DP::DepositRepositoryService, DP::AccountRepositoryService>>,
       kgr::overrides<DP::DepositServiceService> {};

Затем просто добавляем наши сервисы в контейнер:

void TestContainerBootstrapper::initRepos() {
   container_.emplace<LocalAccountRepositoryService>();
   container_.emplace<LocalDepositRepositoryService>();
}

void TestContainerBootstrapper::initServices() {
   container_.emplace<LocalAccountServiceService>();
   container_.emplace<LocalDepositServiceService>();
}

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

Проверим работоспособность с помощью библиотеки Catch2. Тесты должны покрыть следующую функциональность:

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

Вызов функциональных объектов

Помимо внедрения зависимостей в конструкторы kangaru предоставляет возможность вызова функциональных объектов (функций, лямбда-выражений, методов) с помощью контейнера. Делается это путем вызова шаблонного метода invoke у объекта контейнера. В качестве параметров метод принимает функциональный объект и его аргументы. Аргументами шаблона следует сделать классы необходимых сервисов. На практике это выглядит следующим образом:

struct Delegate {
   virtual void sayHello() {
       std::cout << "Hello\n";
   }
};

struct PoliteDelegate : Delegate {
   void sayHello() override {
       std::cout << "Hello, ladies and gentlemen\n";
   }
};

struct DelegateService : kgr::single_service<Delegate>, kgr::polymorphic {};
struct PoliteDelegateService : kgr::single_service<PoliteDelegate>, kgr::overrides<DelegateService> {};

void sayHello(Delegate& delegate) {
   delegate.sayHello();
}

int main() {
   kgr::container container;
   container.invoke<PoliteDelegateService>(sayHello);
   return 0;
}

Функция sayHelloожидает в качестве параметра объект Delegate. Контейнер автоматически инстанциирует нужные объекты и вызовет функциональный объект.

Для упрощения вызовов kangaru предлагает синтаксис отображения сервисов (Map Service). Он позволяет указать, какой сервис необходимо использовать при запросе того или иного аргумента. Выглядит это так:

auto service_map(<parameter>) -> <definition>;
parameter — тип аргумента, definition — соответствующий сервис.

Такой синтаксис позволяет опускать явное указание параметров шаблона при вызове метода invoke.

Важно!Внедрение service_mapдолжно быть в том же пространстве имен, что и аргументы отображения. Иначе будет получена ошибка компиляции.

Теперь давайте позволим клиентам класса Bootstrapperвызывать произвольные функциональные объекты с произвольными сервисами домена.

Внедрим service_mapи соответствующий метод:

namespace Service {
   struct AccountService;
   struct DepositService;
   auto service_map(Service::AccountService&) -> Dependency::AccountServiceService;
   auto service_map(Service::DepositService&) -> Dependency::DepositServiceService;
}
template <typename Callable>
void invoke(Callable callback) {
   container_.invoke(callback);
}

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

bootstrapper.invoke(
       [](Service::AccountService& accounts, Service::DepositService& deposits){
           REQUIRE(accounts.getAccountsAmount() == deposits.getDepositsAmount());
       })

Альтернативный способ описания зависимостей

Напоследок я хотел бы показать альтернативный синтаксис внедрения зависимостей, который называется autowire. Обычно мы внедряем сервисы, явно декларируя зависимости посредством шаблонного типа kgr::dependency:

struct DelegateService : kgr::single_service<Delegate> {};

struct ClientService : kgr::service<Client, kgr::dependency<DelegateService>> {};

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

auto service_map(Delegate const&) -> DelegateService;

Затем просто используем тип kgr::autowireвместо kgr::dependency:

struct Delegate {
   void sayHello() {
       std::cout << "Hello, ladies and gentlemen\n";
   }
};

struct DelegateService : kgr::single_service<Delegate> {};

auto service_map(Delegate&) -> DelegateService;

int main() {
   kgr::container container;

   container.invoke(
           [](Delegate& delegate){
               delegate.sayHello();
           });

   return 0;

Эту форму можно еще упростить, используя автогенерацию сервисов с помощью псевдонимов. В нашем случае контейнер сам сгенерирует singleton-сервис DelegateServiceиз декларации service_map:

#include <kangaru/kangaru.hpp>
#include <iostream>

struct Delegate {
   void sayHello() {
       std::cout << "Hello, ladies and gentlemen\n";
   }
};

auto service_map(Delegate&) -> kgr::single_service<Delegate>;

int main() {
   kgr::container container;

   container.invoke(
           [](Delegate& delegate){
               delegate.sayHello();
           });

   return 0;
}

Другие полезные псевдонимы:

ПсевдонимГенерируемый сервис
kgr::autowire_service<T>kgr::service<T, kgr::autowire>
kgr::autowire_single_service<T>kgr::single_service<T, kgr::autowire>
kgr::autowire_unique_service<T>kgr::unique_service<T, kgr::autowire>
kgr::autowire_shared_service<T>kgr::shared_service<T, kgr::autowire>

[Boost].DI

Один мой коллега говорил: «Если чего-то нет в STL, посмотри в boost». Конечно, boost предоставляет нам фреймворк для эффективного внедрения зависимостей, но пока он не поставляется с официальной сборкой. Как и kangaru, [Boost].DI — header-only-библиотека, и, как утверждают ее авторы, она довольно быстрая по сравнению с аналогичными библиотеками. К особенностям [Boost].DI можно также отнести:

  • возможность явного контроля продолжительности жизни объектов, создаваемых контейнером зависимостей (см. Scopes);
  • управление поведением контейнера, например выбор места аллокации объектов (стек или куча, см. Providers);
  • использование ограничений при наполнении контейнера (см. Concepts).

Как и в случае с kangaru, центральным объектом в [Boost].DI является контейнер зависимостей. В [Boost].DI он называется injector. В отличие от kangaru, его использование лаконичнее: никаких дополнительных деклараций и аннотаций для описания зависимостей не нужно. Базовый пример, описывающий зависимость объекта Client на Delegate, будет выглядеть так:

struct Delegate {
   void sayHello() {
       std::cout << "Hello\n";
   }
};

class Client {
   public:
       explicit Client(Delegate delegate) : delegate_(delegate) {}
       void hello() {
           delegate_.sayHello();
       }

   private:
       Delegate delegate_;
};

int main() {
   auto client = boost::di::make_injector().create<Client>();
   client.hello();
   return 0;
}

Важно!Объект Clientбудет создан на стеке, а затем скопирован. Из этого следует, что для объектов с нетривиальной логикой копирования следует явно определять конструкторы копий, операторы присвоения и перемещения.


struct Delegate {
   void sayHello() {
       std::cout << "Hello\n";
   }

   explicit Delegate(int size) : size_(size), data_(new int[size_]) {
   }

   ~Delegate() {
       delete [] data_;
   }

   Delegate(Delegate& other) : size_(other.size_) {
       memcpy(data_, other.data_, size_);
       std::cout << "copy constructed\n";
   }

   Delegate& operator= (const Delegate& other) {
       if (this == &other) {
           return *this;
       }

       delete [] data_;

       data_ = new int [other.size_];
       memcpy(data_, other.data_, size_);
       size_ = other.size_;

       std::cout << "copy assigned\n";
       return *this;
   }

   Delegate(Delegate&& other) noexcept {
       std::swap(other.data_, data_);
       size_ = other.size_;

       std::cout << "moved constructed\n";
   }

   Delegate& operator= (Delegate&& other) noexcept {
       if (this == &other) {
           return *this;
       }
       std::swap(other.data_, data_);
       size_ = other.size_;

       std::cout << "moved assigned\n";

       return *this;
   }

   int size_ = 0;
   int* data_ = nullptr;
};

class Client {
   public:
       explicit Client(Delegate delegate) : delegate_(std::move(delegate)) {}
       void hello() {
           delegate_.sayHello();
       }

   private:
       Delegate delegate_;
};

int main() {
   auto client = boost::di::make_injector(boost::di::bind<int>().to(10)).create<Client>();
   client.hello();
   return 0;

Привязки интерфейсов к реализациям

Любое взаимодействие с системой должно происходить через интерфейсы (AccountService, DepositService, AccountRepository, DepositRepository). Контейнер должен оперировать имплементациями данных интерфейсов. Привязка интерфейсов к конкретным имплементациям делается в момент конструирования контейнера с помощью шаблонной функции bindследующим образом:

struct Delegate {
   virtual ~Delegate() = default;
   virtual void sayHello() = 0;
};

struct PoliteDelegate : public Delegate {
   void sayHello() override
       std::cout << "Hello, ladies and gentlemen\n";
   }
};

class Client {
   public:
       explicit Client(Delegate& delegate) : delegate_(delegate) {}
       void hello() {
           delegate_.sayHello();
       }

   private:
       Delegate& delegate_;
};

int main() {
   using namespace boost;
   auto client = di::make_injector(di::bind<Delegate>.to<PoliteDelegate>()).create<Client>();
   client.hello();
   return 0;
}

Время жизни объектов

В [Boost].DI жизненный цикл объектов, создаваемых фреймворком, контролируют специальные классы — scopes. Существует 4 вида scope:

  • instance scope — означает использование объектов, которые были переданы в контейнер извне, их продолжительность жизни контролирует пользователь;
  • unique scope — при каждом запросе будет создаваться новый объект;
  • singleton scope — разделяемый глобальный инстанс объекта на весь жизненный цикл приложения;
  • deduce scope — один из вышеописанных вариантов, который выберет фреймворк.

Каждый раз при вызове метода createинжектор создает необходимые объекты, исходя из типа параметров создаваемого объекта. По умолчанию контейнер использует deduce scope, который имеет следующую политику (Object type — тип требуемого объекта, Scope — время жизни созданного объекта):

Object typeScope
Tunique
T&singleton
const T&singleton
T*unique
const T*unique
T&&unique
std::unique_ptrunique
std::shared_ptrsingleton
boost::shared_ptrsingleton
std::weak_ptrsingleton
class scopes_deduction {
   scopes_deduction(const int& /*singleton scope*/,
                    std::shared_ptr<int> /*singleton scope*/,
                    std::unique_ptr<int> /*unique scope*/,
                    int /*unique scope*/)
   {}
};

Кроме того можно явно указывать желаемый scope при создании контейнера вызовом метода in:

injector = di::make_injector(di::bind<Delegate>.to<Delegate>().in(di::singleton));

При этом нужно понимать, что указание scope не может нарушать семантических правил языка. Например, нельзя указывать instance scope, если клиент требует ссылки на объект: это вызовет ошибку компилятора. Это связано с тем, что инжектор создаст временный объект, который нельзя передавать в качестве ссылки.

struct Delegate {
   void sayHello() {
       std::cout << "Hello\n";
   }
};

class Client {
   public:
       explicit Client(Delegate& delegate) : delegate_(delegate) {}
       void hello() {
           delegate_.sayHello();
       }

   private:
       Delegate& delegate_;
};

int main() {
   namespace di = boost::di;

   auto injector = di::make_injector(di::bind<Delegate>.to<Delegate>().in(di::unique));


   // Error: cannot bind temporary object to a reference
   auto client = injector.create<Client>();
   client.hello();
   return 0;
}

Также нужно помнить о том, кто владеет объектом и кто его будет удалять. В первую очередь это касается объектов с unique scope и instance scope:

struct Delegate {

   ~Delegate() {
       std::cout << "~Delegate\n";
   }

   void sayHello() {
       std::cout << "Hello\n";
   }
};

class Client {
   public:
       explicit Client(Delegate* delegate) : delegate_(delegate) {}

       ~Client() {
           // Objects with unique scope must be deleted explicitly
           delete delegate_;
       }

       void hello() {
           delegate_->sayHello();
       }

   private:
       Delegate* delegate_;
};

int main() {
   namespace di = boost::di;
  
   auto injector = di::make_injector(di::bind<Delegate>.to<Delegate>());
auto client = injector.create<Client>();
   client.hello();
   return 0;
}

Контейнер для тестовой конфигурации нашей системы будет иметь следующий вид:

using namespace boost;
auto injector = di::make_injector(
       di::bind<Repository::AccountRepository>.to<Repository::LocalRepo::AccountRepositoryImpl>(),
       di::bind<Repository::DepositRepository>.to<Repository::LocalRepo::DepositRepositoryImpl>(),
       di::bind<Service::AccountService>.to<Service::Impl::AccountServiceImpl>(),
       di::bind<Service::DepositService>.to<Service::Impl::DepositServiceImpl>());

Затем просто создаем объект TestContainerBootstrapper, который имеет зависимость в своем конструкторе от сервисов и репозиториев домена:

TestContainerBootstrapper::TestContainerBootstrapper(
       Service::AccountService& accountService,
       Service::DepositService& depositService,
       Repository::DepositRepository& depositRepository,
       Repository::AccountRepository& accountRepository);

injector.create<TestContainerBootstrapper>();

Динамическое создание требуемых объектов

У [Boost].DI есть интересная возможность — динамическое создание необходимых объектов (run-time) во время запросов к контейнеру. Делается это посредством вызова функцииmake_injector с лямбда-выражением, которое будет вызываться фреймворком для создания запрашиваемых объектов.:

struct Delegate {
   virtual ~Delegate() = default;

   virtual void sayHello() = 0;
};

struct PoliteDelegate : public Delegate {
   void sayHello() override {
       std::cout << "Hello, ladies and gentlemen\n";
   }
};

struct EveningDelegate : public Delegate {
   void sayHello() override {
       std::cout << "Good evening, ladies and gentlemen\n";
   }
};

class Client {
   public:
       explicit Client(Delegate& delegate) : delegate_(delegate) {}
       void hello() {
           delegate_.sayHello();
       }

   private:
       Delegate& delegate_;
};

int main() {
   using namespace boost;
   bool is_evening = false;

   auto injector = di::make_injector(
           di::bind<Delegate>().to([&](const auto& injector) -> Delegate& {
               if (is_evening)
                   return injector.template create<PoliteDelegate&>();
               else
                   return injector.template create<EveningDelegate&>();
           })
   );

   injector.create<Client>().hello();
   return 0;
}

Другие типы зависимостей

Помимо зависимостей к пользовательским типам [Boost].DI позволяет создавать зависимости к фундаментальным типам (int, float, double, char):

struct Client {
   Client(int i, double j, char c) { std::cout << i << ' ' << j << ' ' << ' ' << c; }
};

int main() {
   auto injector = boost::di::make_injector(
           boost::di::bind<int>.to(42),
           boost::di::bind<double>.to(3.14),
           boost::di::bind<char>.to('a'));

   injector.create<Client>();

   return 0;
}

Можно создавать зависимости к уже существующим объектам. Это может быть полезным при работе с third-party-библиотеками в случаях, когда логика создания объектов скрыта от пользователя:

struct Delegate {
   void sayHello() {
       std::cout << "Hello, ladies and gentlemen\n";
   }
};

struct Client {
   explicit Client(Delegate& delegate) : delegate_(delegate) {}
   void hello() {
       delegate_.sayHello();
   }

   Delegate& delegate_;
};

int main() {

   Delegate global_instance;

   auto injector = boost::di::make_injector(boost::di::bind<Delegate>.to(global_instance));

   Client first = injector.create<Client>();
   Client second = injector.create<Client>();

   assert(&first.delegate_ == &second.delegate_);

   return 0;
}

Возможно даже указывать зависимости к аргументам шаблонных классов, что является весьма полезным при шаблонном метапрограммировании:

struct Delegate {
   void sayHello() {
       std::cout << "Hello\n";
   }
};

struct PoliteDelegate {
   void sayHello() {
       std::cout << "Hello, ladies and gentlemen\n";
   }
};


template <typename T = class TDelegate>
struct Client {
   void hello() {
       T delegate;
       delegate.sayHello();
   }
};

int main() {

   auto injector = boost::di::make_injector(boost::di::bind<class TDelegate>.to<PoliteDelegate>());
   injector.create<Client>().hello();

   return 0;
}

Заключение

Рассмотренные DI-фреймворкииспользуются в реальных проектах. На мой взгляд, [Boost].DI имеет API, который позволяет лучше управлять контейнером зависимостей. Оба фреймворка дают возможность описания зависимостей без модификации классов сущностей или создания XML-конфигураций.Это преимущество позволяет заменять библиотеки с минимальными затратами в процессе разработки.

Полезные ссылки


Приклад gRPC-мікросервісу на Go

$
0
0

Привіт, мене звуть Ярослав, я працюю в компанії Evrius. Прийшовши в проект, отримав завдання: розробити мікросервіс для збереження статистики. Тому розпочав вивчати gRPC.

Фреймворк gRPC (remote procedure call) — продукт Google, розроблений для стандартизації взаємодії між сервісами й зменшення обсягу трафіку. gRPC розглядаю як хорошу заміну REST під час взаємодії між мікросервісами. У gRPC лаконічний формат опису, порівняно з Swagger є backward і forward compatibility, а також автогенерація коду популярними мовами програмування (у Swagger автогенерація теж є).

Тому стаття буде цікава тим, хто вже щось чув хороше про gRPC і хоче впровадити його в проект. У статті описано просте завдання й докладне його розв’язання.

Якщо вже є досвід з gRPC, то можете завантажити репозиторійі запустити проект.

Завдання і налаштування проекту

Розробити сервіс для збереження рецептів і пошук рецепта за інгредієнтами. Наприклад, зберегти рецепти салатів і знайти рецепт, де є моцарела. Працюю з тестами, тому створю проект і запущу простий тест. Вибрав пакетний менеджер dep (бо його використовуємо в основному проекті):

dep init

Команда створює файли Gopkg.toml, Gopkg.lock у корені проекту:

~/go/src/gitlab.com/go-yp/grpc-recipes
├── Gopkg.lock
└── Gopkg.toml

А ще під’єднуємо пакет для assert-ів:

dep ensure -add github.com/stretchr/testify/assert

Напишемо й запустимо тест:

package tests

import (
	"github.com/stretchr/testify/assert"
	"testing"
)

func TestDefault(t *testing.T) {
	assert.Equal(t, 1, 1)
}

go test ./components/... -v

=== RUN   TestDefault
--- PASS: TestDefault (0.00s)
PASS
ok  	gitlab.com/go-yp/grpc-recipes/components/tests	0.002

Опишемо proto-файли для сервісу рецептів:

# protos/services/recipes/recipes.proto
syntax = "proto3";

package recipes;

message Empty {
}

message Ingredient {
    uint32 code = 1;
    string name = 2;
}

message Recipe {
    uint32 code = 1;
    string title = 2;
    repeated Ingredient ingredients = 3;
}

message Recipes {
    repeated Recipe recipes = 1;
}

message IngredientsFilter {
    repeated uint32 codes = 1;
}

service RecipesService {
    rpc Store (Recipes) returns (Empty);
    rpc FindByIngredients (IngredientsFilter) returns (Recipes);
}

Отже, є сервіс RecipesService з методами Store й FindByIngredients, де методи отримують і повертають повідомлення.

На основі proto-файлу protos/services/recipes/recipes.proto можемо згенерувати Go-файл, що міститиме структури message, RecipesService-клієнт й інтерфейс сервера RecipesService.

Для генерації потрібно protoc compiler, за посиланням інструкція з налаштування. Після того як встановили protoc, можемо запустити команду для генерації Go-файлу:

mkdir models
protoc -I . protos/services/recipes/*.proto --go_out=plugins=grpc:models

Отже, проаналізуймо згенерований Go-файл models/protos/services/recipes/recipes.pb.go:

// Code generated by protoc-gen-go. DO NOT EDIT.
// source: protos/services/recipes/recipes.proto

package recipes

type Empty struct {
	XXX_NoUnkeyedLiteral struct{} `json:"-"`
	XXX_unrecognized     []byte   `json:"-"`
	XXX_sizecache        int32    `json:"-"`
}

type Ingredient struct {
	Code                 uint32   `protobuf:"varint,1,opt,name=code,proto3" json:"code,omitempty"`
	Name                 string   `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"`
	XXX_NoUnkeyedLiteral struct{} `json:"-"`
	XXX_unrecognized     []byte   `json:"-"`
	XXX_sizecache        int32    `json:"-"`
}

type Recipe struct {
	Code                 uint32        `protobuf:"varint,1,opt,name=code,proto3" json:"code,omitempty"`
	Title                string        `protobuf:"bytes,2,opt,name=title,proto3" json:"title,omitempty"`
	Ingredients          []*Ingredient `protobuf:"bytes,3,rep,name=ingredients,proto3" json:"ingredients,omitempty"`
	XXX_NoUnkeyedLiteral struct{}      `json:"-"`
	XXX_unrecognized     []byte        `json:"-"`
	XXX_sizecache        int32         `json:"-"`
}

type Recipes struct {
	Recipes              []*Recipe `protobuf:"bytes,1,rep,name=recipes,proto3" json:"recipes,omitempty"`
	XXX_NoUnkeyedLiteral struct{}  `json:"-"`
	XXX_unrecognized     []byte    `json:"-"`
	XXX_sizecache        int32     `json:"-"`
}

type IngredientsFilter struct {
	Codes                []uint32 `protobuf:"varint,1,rep,packed,name=codes,proto3" json:"codes,omitempty"`
	XXX_NoUnkeyedLiteral struct{} `json:"-"`
	XXX_unrecognized     []byte   `json:"-"`
	XXX_sizecache        int32    `json:"-"`
}

Ті ж структури, що описано в proto-файлі:

type RecipesServiceClient interface {
	Store(ctx context.Context, in *Recipes, opts ...grpc.CallOption) (*Empty, error)
	FindByIngredients(ctx context.Context, in *IngredientsFilter, opts ...grpc.CallOption) (*Recipes, error)
}

type recipesServiceClient struct {
	cc *grpc.ClientConn
}

func NewRecipesServiceClient(cc *grpc.ClientConn) RecipesServiceClient {
	return &recipesServiceClient{cc}
}

func (c *recipesServiceClient) Store(ctx context.Context, in *Recipes, opts ...grpc.CallOption) (*Empty, error) {
	out := new(Empty)
	err := c.cc.Invoke(ctx, "/recipes.RecipesService/Store", in, out, opts...)
	if err != nil {
		return nil, err
	}
	return out, nil
}

func (c *recipesServiceClient) FindByIngredients(ctx context.Context, in *IngredientsFilter, opts ...grpc.CallOption) (*Recipes, error) {
	out := new(Recipes)
	err := c.cc.Invoke(ctx, "/recipes.RecipesService/FindByIngredients", in, out, opts...)
	if err != nil {
		return nil, err
	}
	return out, nil
}

Готовий для використання клієнт, що його створюють через NewRecipesServiceClient:

type RecipesServiceServer interface {
	Store(context.Context, *Recipes) (*Empty, error)
	FindByIngredients(context.Context, *IngredientsFilter) (*Recipes, error)
}

func RegisterRecipesServiceServer(s *grpc.Server, srv RecipesServiceServer) {
	s.RegisterService(&_RecipesService_serviceDesc, srv)
}

func _RecipesService_Store_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
	in := new(Recipes)
	if err := dec(in); err != nil {
		return nil, err
	}
	if interceptor == nil {
		return srv.(RecipesServiceServer).Store(ctx, in)
	}
	info := &grpc.UnaryServerInfo{
		Server:     srv,
		FullMethod: "/recipes.RecipesService/Store",
	}
	handler := func(ctx context.Context, req interface{}) (interface{}, error) {
		return srv.(RecipesServiceServer).Store(ctx, req.(*Recipes))
	}
	return interceptor(ctx, in, info, handler)
}

func _RecipesService_FindByIngredients_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
	in := new(IngredientsFilter)
	if err := dec(in); err != nil {
		return nil, err
	}
	if interceptor == nil {
		return srv.(RecipesServiceServer).FindByIngredients(ctx, in)
	}
	info := &grpc.UnaryServerInfo{
		Server:     srv,
		FullMethod: "/recipes.RecipesService/FindByIngredients",
	}
	handler := func(ctx context.Context, req interface{}) (interface{}, error) {
		return srv.(RecipesServiceServer).FindByIngredients(ctx, req.(*IngredientsFilter))
	}
	return interceptor(ctx, in, info, handler)
}

var _RecipesService_serviceDesc = grpc.ServiceDesc{
	ServiceName: "recipes.RecipesService",
	HandlerType: (*RecipesServiceServer)(nil),
	Methods: []grpc.MethodDesc{
		{
			MethodName: "Store",
			Handler:    _RecipesService_Store_Handler,
		},
		{
			MethodName: "FindByIngredients",
			Handler:    _RecipesService_FindByIngredients_Handler,
		},
	},
	Streams:  []grpc.StreamDesc{},
	Metadata: "protos/services/recipes/recipes.proto",
}

Інтерфейс RecipesServiceServer треба зреалізувати й під’єднати через RegisterRecipesServiceServer. Після реалізації RecipesServiceServer і запуску gRPC матимемо готовий сервіс RecipesServer.

В інший сервіс, хай буде Core, ми додамо proto-файл, згенеруємо Go-файл з клієнтом RecipesServiceClient, і сервіс Core зможе робити запити на RecipesServer. Тепер структура проекту така:

~/go/src/gitlab.com/go-yp/grpc-recipes
├── components
│   └── tests
│       └── grpc_test.go
├── Gopkg.lock
├── Gopkg.toml
├── models
│   └── protos
│       └── services
│           └── recipes
│               └── recipes.pb.go
├── protos
│   └── services
│       └── recipes
│           └── recipes.proto
└── vendor

Реалізація Recipes-сервісу

Створимо файл components/server/server.go:

package server

type Server struct {
}

Згенеруємо методи інтерфейсу RecipesServiceServer за допомогою Goland IDE, натискаємо комбінацію Ctrl+I ― з’являється поле, де вводимо назву RecipesServiceServer, тоді отримуємо:

package server

import (
	"context"
	"gitlab.com/go-yp/grpc-recipes/models/protos/services/recipes"
)

type Server struct {
}

func (Server) Store(context.Context, *recipes.Recipes) (*recipes.Empty, error) {
	panic("implement me")
}

func (Server) FindByIngredients(context.Context, *recipes.IngredientsFilter) (*recipes.Recipes, error) {
	panic("implement me")
}

Тепер допишемо логіку додавання рецептів і пошуку за інгредієнтами:

package server

import (
	"context"
	"gitlab.com/go-yp/grpc-recipes/models/protos/services/recipes"
	"sync"
)

var (
	empty = &recipes.Empty{}
)

type Server struct {
	mu   sync.RWMutex
	data []*recipes.Recipe
}

func (s *Server) Store(ctx context.Context, recipes *recipes.Recipes) (*recipes.Empty, error) {
	s.mu.Lock()
	s.data = append(s.data, recipes.Recipes...)
	s.mu.Unlock()

	return empty, nil
}

func (s *Server) FindByIngredients(ctx context.Context, filter *recipes.IngredientsFilter) (*recipes.Recipes, error) {
	result := make([]*recipes.Recipe, 0)

	s.mu.RLock()
	data := s.data
	s.mu.RUnlock()

	codeMap := make(map[uint32]bool, len(filter.Codes))
	for _, code := range filter.Codes {
		codeMap[code] = true
	}

	for _, recipe := range data {
		for _, ingredient := range recipe.Ingredients {
			if codeMap[ingredient.Code] {
				result = append(result, recipe)

				break
			}
		}
	}

	return &recipes.Recipes{
		Recipes: result,
	}, nil
}

Напишемо тест і перевіримо, чи працює. Додамо два пакети protobufі grpc, які використовують у файлі models/protos/services/recipes/recipes.pb.go в Gopkg.toml.

[[constraint]]
  name = "google.golang.org/grpc"
  version = "1.18.0"

[[constraint]]
  branch = "master"
  name = "github.com/golang/protobuf"

Виконаємо команду:

dep ensure

Версії конфліктують між собою, тому й використовую master branch для пакета github.com/golang/protobuf.

Для тестування gRPC є пакет google.golang.org/grpc/test/bufconn, готування до тестування має такий вигляд:

# components/tests/grpc_test.go
package tests

import (
	"context"
	"github.com/juju/errors"
	"github.com/stretchr/testify/assert"
	"gitlab.com/go-yp/grpc-recipes/components/server"
	"gitlab.com/go-yp/grpc-recipes/models/protos/services/recipes"
	"google.golang.org/grpc"
	"google.golang.org/grpc/test/bufconn"
	"log"
	"net"
	"testing"
)

const (
	bufferSize = 1024 * 1024
)

func TestStoreAndFindByIngredients(t *testing.T) {
	connection, err := mockServerConnect(context.Background())
	if !assert.NoError(t, err) {
		return
	}
	defer connection.Close()

	client := recipes.NewRecipesServiceClient(connection)

	_ = client
}

func mockServerConnect(ctx context.Context) (conn *grpc.ClientConn, err error) {
	lis := bufconn.Listen(bufferSize)
	s := grpc.NewServer()

	recipes.RegisterRecipesServiceServer(
		s,
		new(server.Server),
	)

	go func() {
		if err := s.Serve(lis); err != nil {
			log.Fatalf("[CRITICAL] Server exited with error: %+v", errors.Trace(err))
		}
	}()

	return grpc.DialContext(
		ctx,
		"bufnet",
		grpc.WithContextDialer(func(context.Context, string) (net.Conn, error) {
			return lis.Dial()
		}),
		grpc.WithInsecure(),
	)
}

А тепер протестуємо додавання рецепта й пошук:

func TestStoreAndFindByIngredients(t *testing.T) {
	connection, err := mockServerConnect(context.Background())
	if !assert.NoError(t, err) {
		return
	}
	defer connection.Close()

	client := recipes.NewRecipesServiceClient(connection)

	recipe1 := &recipes.Recipe{
		Code:  10001,
		Title: "Борщ",
		Ingredients: []*recipes.Ingredient{
			{
				Code: 625,
				Name: "Буряк",
			},
			{
				Code: 725,
				Name: "Квасоля",
			},
			{
				Code: 675,
				Name: "Помідори",
			},
		},
	}

	recipe2 := &recipes.Recipe{
		Code:  10002,
		Title: "Вінегрет з печерицями",
		Ingredients: []*recipes.Ingredient{
			{
				Code: 625,
				Name: "Буряк",
			},
			{
				Code: 825,
				Name: "Печериці",
			},
		},
	}

	mainRecipes := &recipes.Recipes{
		Recipes: []*recipes.Recipe{
			recipe1,
			recipe2,
		},
	}

	storeResponse, err := client.Store(context.Background(), mainRecipes)
	if !assert.NoError(t, err) {
		return
	}
	assert.Equal(t, &recipes.Empty{}, storeResponse)

	recipesBy625, err := client.FindByIngredients(context.Background(), &recipes.IngredientsFilter{
		Codes: []uint32{625},
	})
	if !assert.NoError(t, err) {
		return
	}
	assert.Equal(t, mainRecipes, recipesBy625)
}

А пам’ятаєте, що в згенерованих структурах з proto-файлу були додаткові службові XXX-поля?То ось через XXX-полятест провалився на assert.Equal(t, mainRecipes, recipesBy625). Допишемо порівняння (звісно, є припущення, що хтось уже написав автогенерацію таких порівнянь).

func TestStoreAndFindByIngredients(t *testing.T) {
	// ...
	assertEqualRecipes(t, mainRecipes.Recipes, recipesBy625.Recipes)
}

func assertEqualRecipes(t *testing.T, expect, actual []*recipes.Recipe) bool {
	t.Helper()

	if !assert.Equal(t, len(expect), len(actual)) {
		return false
	}

	for i := range expect {
		if !assert.Equal(t, expect[i].Code, actual[i].Code) {
			return false
		}

		if !assert.Equal(t, expect[i].Title, actual[i].Title) {
			return false
		}

		if !assertEqualIngredient(t, expect[i].Ingredients, actual[i].Ingredients) {
			return false
		}
	}

	return true
}

func assertEqualIngredient(t *testing.T, expect, actual []*recipes.Ingredient) bool {
	t.Helper()

	if !assert.Equal(t, len(expect), len(actual)) {
		return false
	}

	for i := range expect {
		if !assert.Equal(t, expect[i].Code, actual[i].Code) {
			return false
		}

		if !assert.Equal(t, expect[i].Name, actual[i].Name) {
			return false
		}
	}

	return true
}

Вітаю, тепер тести виконано.

Перевіримо localhost

Ми запустимо сервер на localhost і за допомогою тестів перевіримо, що так само працює як і через bufconn.

Створимо файл main.go:

package main

import (
	"github.com/juju/errors"
	"gitlab.com/go-yp/grpc-recipes/components/server"
	"gitlab.com/go-yp/grpc-recipes/models/protos/services/recipes"
	"google.golang.org/grpc"
	"log"
	"net"
)

func main() {
	lis, err := net.Listen("tcp", ":32625")
	if err != nil {
		log.Fatalf("[CRITICAL] failed to listen: %+v", errors.Trace(err))
	}
	defer lis.Close()

	s := grpc.NewServer()

	recipes.RegisterRecipesServiceServer(
		s,
		new(server.Server),
	)

	if err := s.Serve(lis); err != nil {
		log.Fatalf("[CRITICAL] Server exited with error: %+v", errors.Trace(err))
	}
}

Оновимо тест і побачимо, що змінилося:

func TestStoreAndFindByIngredients(t *testing.T) {
	// connection, err := mockServerConnect(context.Background())
	connection, err := localhostServerConnect("localhost:32625")
	if !assert.NoError(t, err) {
		return
	}
	defer connection.Close()
	// ...
	// same
}

func localhostServerConnect(address string) (conn *grpc.ClientConn, err error) {
	return grpc.Dial(address, grpc.WithInsecure())
}

В окремому вікні терміналу запустимо:

go run main.go

Знову запускаю тести ― усе вдалося.

Епілог

Готовий репозиторій з прикладом можна переглянути на GitLab-і. Під час написання фокус робив саме на gRPC. Сподіваюся, що все вдалося.

А ще ми до себе в команду шукаємо Go розробника.

Кремнієва долина Китаю. Як програмісту живеться у Шеньчжені

$
0
0

Мене звуть Нік Турунов, після закінчення КПІ (а це було у 2002-му,ще до зародження українського аутсорсу в його теперішньому стані) я влаштувався комп’ютерним майстром на всі руки. Згодом відкрив для себе журналістику і певний час працював редактором на DOU, аж поки доля не привела мене в EPAM. Там я вирішив повернутися до свого початкового фаху і підівчився на програміста, який відповідає сучасним вимогам. П’ять років тому я переїхав до нещодавно відкритого (на той час) офісу компанії в місті Шеньчжень, що сусідить із Гонконгом. У статті розповім, що спонукало мене змінювати фахи та як мені наразі живеться у Китаї.

Про шлях із IT до журналістики і назад

У 2004 році я закінчив факультет прикладної математики в КПІ. Тепер мені здається, що то був переломний час. Умовно: моє покоління відгрібало ще старі технології й підходи, моя освіта була радше академічною, хоча вже наступні випуски більше заточували на прикладні речі. Ринок праці програмістів уже почав формуватися, але був ще досить сирим, тож після випуску всі мої одногрупники розбрелися хто куди. Уся IT-аутсорс тусовка в Україні або ж перебувала в зародковому стані, або ж просто пройшла повз мене. Я ж спочатку для старту «крутив компи», потім влаштувався енікейщиком на всі руки: збери-пофарбуй-зроби сайт/макет-проклади сітку.

Аж одного дня мені до рук потрапив журнал CHIP — ще раніше читав його студентом. Гортаючи свіжий випуск, знайшов декілька знайомих прізвищ — хлопців з факультету, всього на рік-два молодших за мене. Зацікавився, сконтактував. «Приходь до нас», — сказали. Запропонували для початку виконати тестове завдання. Оскільки в мене з письмом завжди складалося добре, впорався. Запропонували перекваліфікуватися з айтішника в журналісти. Я пристав на цю пропозицію і наступні кілька років писав про світ гаджетів та інші прикладні речі, змінив декілька видань і взагалі радше мріяв про журналістику, власне видання про гітари тощо. Та криза 2009 року виявила, наскільки зарплата журналіста залежить від рекламодавців, і в мене з’явилися перші сумніви...

2009 року я став фрилансером, знову ж «на всі руки»: писав статті, наваяв декілька книжок про софт для маків, потроху колупався у веб-дизайні та програмуванні, допомагав товаришеві робити лендинги для гітарних брендів, а у вільний час грав у групах. Крім того, тодішній редактор DOU Андрій Дегелер, мій хороший знайомий, підкидав різні завдання: писати статті, брати інтерв’ю, що я залюбки і робив. Був я таким собі штатно-нештатним працівником, щотижня в мене виходила стаття. Але чим більше по роботі мені доводилося мати справу зі світом справжнього, «дорослого» IT, тим частіше я починав замислюватися, чи не податися знову в програмування.

Тоді якраз декілька наших аутсорсерів запустили програми навчання для джунів. На одній з конференцій я проходив повз стенд EPAM, зачепився за знайомих, забалакався, і мені запропонували податися на такі курси з Java при КПІ. Я сказав собі — чом би й ні? Було цікаво: щось ще пам’ятав, щось дізнався заново, щось схоплював на льоту. Загалом я не знав багатьох сучасних (тоді) речей, курси висвітлили прогалини у моїй освіті. Відтак потрапив до лабораторії в EPAM... а там мені запропонували піти штатним журналістом до медіавідділу. Це краще, ніж йти java-джуном, подумав я і погодився.

Півтора року працював там на посаді медіаменеджера, займаючись, власне, тим що звик робити. Але колупати код мені було цікавіше, тож я відвідував різні технічні конференції, мітапи, яких було багато в компанії, просто спілкувався з хлопцями за кавою на технічні теми. Підучив JS, Angular, почав робити сайтики для наших внутрішніх конференцій, набирався знань, щоб знову змінити фах.

Одразу за мостом починається Гонконг. Від кордону до центру — десь 40-50хвилин автобусом. Багато хто їздить на роботу у Гонконг (або навпаки) кожного ранку

Про переїзд до Китаю й адаптацію

Тоді пара моїх близьких знайомих вже чотири роки перебувала у Китаї, у місті Шеньчжень — одному з найбільших портів у країні, на кордоні з Гонконгом. Якось запросили до себе на Новий рік, я приїхав, відвідав заодно Гонконг, погуляв — мені дуже сподобалося. На той час ще не велося розмов про офіс EPAM у Китаї, але в травні 2014-гойого відкрили. Я потирав ручки: рідні місця, все знаю. Товариш із Києва поїхав туди «освоювати цілину», а потім запропонував їхати і мені. То була нагода знову переметнутися в програмісти. Пройшов технічне інтерв’ю на позицію Software Engineer по скайпу та у вересні 2014-гоотримав офер. Я не був ані до чого фізично, емоційно, матеріально прив’язаний, та й люблю подорожувати, змінювати обстановку, тож не вагався ані хвилини. Відколи з січня 2015 року я тут, ще жодного разу не пошкодував про це.

Процес отримання документів в Україні був досить муторним. Компанія офіційно релокувала, тож моя присутність була потрібна лише тоді, коли без неї не обійтися: при поданні документів на візу в посольстві та в лікарні при отриманні довідки. Останнє виснажило найбільше. Медичну довідку для виїзду за кордон можна було отримати в єдиному місці: в Олександрівській клінічній лікарні (центральній міській лікарні Києва). То був справжній квест: приходиш, тобі дають обхідний лист, і з ним треба бігати із будівлі до будівлі, із кабінету до кабінету, потім зробити кілька обов’язкових процедур (аналіз на СНІД, який роблять у лабораторії на іншому кінці Києва; флюорографію тощо). На все це знадобилося 2-3 тижні,дуже повільно все рухалося.

Приїхав до Китаю. Там для отримання робочої візи теж потрібна місцева медична довідка. Мене привели до лікарні, майже за руку провели усіма потрібними кабінетами, здав кров на аналіз за п’ять хвилин. На все знадобилося дві години (включно з дорогою до офісу і назад). Що, так можна було?

Я завжди вважав себе людиною без особливих культурних прив’язок, і в Китаї це підтвердилося: ані до чого не довелося звикати. Перший рік-півтора ходив вулицями у захваті із роззявленим ротом. Мене вражало все: чудове сучасне місто з неймовірною архітектурою, зручна інфраструктура з мостами і транспортними розв’язками, до Гонконгу їхати годину, повсюди зелень, парки, набережна на 20 кілометрів вздовж затоки, абсолютна безпека і повний дзен.

Перші місяці я трохи страждав без знання китайської. Хоча з мовами в мене завжди все було гаразд: в анамнезі маю, крім досить прокачаної англійської, іспанську зі школи, курси німецької і японську, якою я завжди захоплювався і намагався вчити самотужки. Порівняно легко і швидко мені зайшла й китайська, причому я її навіть не вчив спеціально, а опановував у процесі. Слухав, аналізував, вникав у суть граматичних конструкцій. Якось досить легко навчився імітувати звуки. Ходив на чайний ринок пити чайок та практикуватися з місцевими. Я не можу сказати, що знаю китайську добре, але достатньо для вирішення більшості побутових ситуацій та якихось смолтоків. Втім, кожна потреба щось зробити змушує мене поповнювати словниковий запас. Це ніби вчитися плавати, коли опинився на глибині.

Багато знайомих експатів не знає китайською ані слова. З англійською тут можна вижити. Не всюди ідеально розмовлятимуть, але порозумітися можна, смартфони з вічатом та онлайн-перекладачем мають усі. Кожну вивіску дубльовано, так само з навігацією у метро, на вулицях.

Набережна уздовж Shenzhen Bay простяглася на двадцять з гаком кілометрів, пішохідні та велосипедні зони плавно переходять в парки. Через затоку, буквально за півтора кілометра — Гонконг

Житло

Компанія забезпечувала перший місяць оплаченого перебування в service apartment — така собі кімната готельного типу, маленька, затишна, 15 хвилин автобусом до роботи, в межах району, де розташовано офіс. Крім цього, оплачувала переліт та надавала підйомні гроші.

Загалом знайти квартиру тут зовсім не проблема, навіть неподалік від роботи. Звісно, якщо нема особливих примх, бо трапляються різні помешкання, більш і менш зручні. Моя оселя розташована в районі Шекоу, де багато експатів. Пішов до агента (серед них багато англомовних), вони показували варіанти за заданими параметрами, за день я уклав контракт. У моїх друзів та знайомих все теж відбувалося досить просто. Не пригадую, щоб хтось шукав помешкання довше тижня. Відтоді я переїжджав чотири рази, причому тричі в межах одного будинку — або контракт закінчувався, або у власника щось траплялося, забирав маму до Шеньчженю тощо.

Ціна дуже залежить від району. Шекоу досить недешевий, але зручний для мене. В середньому нормальна однушка тут коштуватиме 5000-6000 юанів,або ж 600-700 доларів.Десь подалі на периферії за ці гроші можна зняти і ніфігові хороми в 100 квадратів. Це дешево порівняно з Гонконгом, де дикі ціни на нерухомість, а хатинки досить малі.

Проте щоб придбати тут житло, треба вирощувати нирки, як шампіньйони. Ціни стартують з 800 тисяч доларів. Не знаю жодного експата, який власноруч заробив би на власне помешкання. Але для змішаної подружньої пари то не проблема — для громадян створено досить непогані іпотечні умови та є багато програм допомоги від держави.

Кожен із новеньких хмарочосів — свого роду величезний світлодіодний екран, і щовечора будівлі стають «учасниками» світломузичної вистави

IT-ринок і технології

Шеньчжень — це така собі «кремнієва долина» Китаю, але більше спрямована на «залізо». Тут найбільший ринок електроніки, компонентів до неї, мобільних телефонів. Багато стартапів, банківських проектів, сервісів, робототехніки. Один із прикладів, який втілювали мої знайомі програмісти: в більшості гонконгських готелів існує сервіс оренди телефонів для туристів, які не мають місцевого мобільного зв’язку. У телефоні є карта тамтешніх видатних місць, доступ до інтернету, навігація — все, щоб людина могла комфортно пересуватися містом. Так ось, прошивку писали в Шеньчжені. В бізнес-парку, де розташований наш офіс, є представництва DJI, Alibaba, китайський Amazon, Expedia.

Зарплати айтішників тут непогані, абсолютних цифр навести не можу, бо чув різне, до того ж Шеньчжень — досить недешеве місто, але загалом айті тут у фаворі. Місцеві програмісти залюбки йдуть до іноземних контор — у них рівень life-work balance значно вищий, у китайських же його часто не існує: ненормований робочий день, відпустка лише на китайський новий рік. У країні багато технічних університетів, які готують фахівців. Але я б не сказав, що айтішники вважають себе тут представниками upper middle класу і «ілітою» — така собі скромна гідна професія, але працювати в банку куди престижніше.

В середньому $45K на рік отримує Senior Software Engineer у Шеньчжені згідно з Glassdoor (7000 CNY = 1000 USD)

На нас локальна китайська корпоративна культура майже не впливає, робочий процес нашого офісу не надто відрізняється від аналогічного... та будь-де: все залежить від проекту, але в середньому досить розмірено. Найбільше в команді китайців, відсотків 20-30 приїжджі:індуси, угорці, білоруси, українці, росіяни.

Країна має проблеми з доступом до інтернету, закриті гугл-сервіси, фейсбук. Люди використовують VPN, і жоден з цього приводу не заморочується. Китай, який я бачу, дуже прогресивний і технологічний. Гадаю, якщо у світі запустять телепорт, у Китаї він з’явиться через тиждень, а вже за місяць всі сприйматимуть це як даність. Навіть моя улюблена Японія в цьому плані дещо програє, там населення більш консервативне...

Особливо мені тут подобається рівень прийняття технологій. Я взагалі не тримав готівки у руках років з чотири. Та й то лише тому, що в перший рік мого перебування не звик, що можна по-іншому. Тут неймовірний рівень технологічної інфраструктури, додатків, онлайн-сервісів, додатків для доставки їжі. Мало хто користується навіть картками, 95% покупок здійснюють через Wechat, Alipay — така опція можлива навіть у найменших магазинах. Навіть у бабусі, яка продає лічіна розі, є QR-код. Транспорт можна оплатити декількома способами, в тому числі телефонним додатком. Проїзд в метро й автобусах зональний, але дуже недорогий — проїхатися на інший кінець міста (дві години на метро) — це максимум 1,5 долари. Таксі (або місцевий аналог Убера — Діді) теж недороге і тому дуже популярне.

На роботу мені зручно діставатися на метро: 5 зупинок + менше десяти хвилин пішки до станції. Коли я лише приїхав, у місті ще траплялися бензинові автобуси, тепер же їх усі замінили на електричні. Практично така ж ситуація з таксі: у перший рік мого життя тут бензинових і електричних було 50 на 50, тепер — 5 на 95. Відчувається, що в Шеньчжені чисте повітря, жодних вихлопів. У місто багато інвестують, Шеньчжень є таким собі майданчиком для тестування. Скажімо, днями тут офіційно запустили 5G.

На одному маршруті ввели навіть self-driving bus — безпілотний міський автобус, в тестовому режимі. Налагоджено сервіс оренди байків, кілька років тому він еволюціонував, позбавившись прив’язки до станцій. Велосипед можна залишити де завгодно і взяти так само: додаток показує найближчий у радіусі кілометра транспорт.

Люди

Традиційна китайська робоча культура для європейця може здатися дикуватою. Якось я зранку йшов на роботу повз ресторан і побачив, як адміністрація закладу вишикувала в шеренгу кухарів і кричала їм щось, вочевидь, мотивуючи так до роботи. Але тут не поскаржишся, як у Європі, що багато чого зачинено: все працює майже цілодобово. Повертаючись о третій ночі додому, можна купити собі фруктів у крамниці, поїсти в кафе тощо.

Але загалом у поведінці тутешніх не відчувається агресії, нема «побутового мудацтва». Випадок із першого року проживання у Шеньчжені: забилася труба в квартирі, попросив лендлорда з гарною англійською допомогти. Той викликав сантехніка, який англійською — ані слова. Він не розгубився, дістав телефон із перекладачем — знайшли канал комунікації, все було напрочуд адекватно. Мені складно уявити зворотну ситуацію, коли, наприклад, китаєць у якійсь українській провінції викликає додому українського сантехніка...

Китайці досить відкриті, приязні, допитливі, люблять потусити, але в міру. Вони мобільні, легко переїжджають із місця на місце, в них нема установок на кшталт «тут народився, тут житиму і помру». Шеньчжень є таким собі котлом із приїжджих, тут мало корінного населення. Місто, що вважається технологічним хабом, приваблює на роботу молодь, вона їде сюди заробляти.

Але що розчарувало саме мене — в китайців досить мало хобі, для дорослих людей небагато тусовок за інтересами. Можуть хіба що якимось спортом займатися. Натомість для дітей багато дозвілля: музичні школи на кожному кроці, художні школи, тхеквондо, балет. Не знаю, хто б з дорослих там цим займався.

Напевно, найбільша розвага китайців — їжа: зібратися великою компанією в ресторані, поїсти, випити, поспівати у караоке. Китайці напрочуд музичні: на вихідних у парку попід кущиками сидітимуть бабусі й дідусі зі скрипками й акордеонами. Хтось грає у карти, хтось займається тайцзіцюань.

Про розвиток міста

Чотири роки тому на цьому місці був пустир, тепер - район хмарочосівЧотири роки тому на цьому місці був пустир, тепер — район хмарочосів
Ще 40 років тому на місці теперішнього міста були рибальські села. На розвиток міста дуже вплинула близькість до Гонконгу — це така собі відповідь Китаю Гонконгу. Коли Шеньчжень почав розвиватися, його проголосили особливою економічною зоною, багато зусиль доклали до розвитку міжнародної торгівлі. У провінції Гуандун, де розташований Шеньчжень, багато промисловості, заводів.


Шеньчжень має свій аеропорт, місто також обслуговує аеропорт Гонконгу, до якого теж досить просто дістатися. Навіть не перетинаючи межі Гонконгу, я можу на поромі припливти додому. Аеропорт у Шеньчжені розташований у межах міста, мені на метро до нього 40 хвилин. Є багато лоукостів по Азії: скажімо, переліт до Таїланду і назад обійдеться у 100 доларів. Натомість дорогі перельоти Китаєм: рейс до Пекіна у середньому коштує 500 доларів. Для порівняння: подорож до Сан-Франциско вартує стільки ж. Якщо ж моніторити сайти і чекати на знижки від авіакомпаній, то завжди знайдеться куди поїхати у відпустку, та навіть злітати на вихідні на Борнео — це цілком нормально. Також у Шеньчжені звели розкішний порт, у ньому є лайнери, які доправлять до В’єтнаму, Японії. Я за п’ять років об’їхав майже всі країни регіону (крім Північної Кореї і Брунею, які теж в планах).

Про плани

Я не можу сказати, що Китай є країною моєї мрії, не планую назавжди тут лишитися. Шеньчжень є досить комфортним для життя, і тут дуже багато плюсів, але залишатися, якщо ти не «пустив коріння», навряд чи захочеться. Тут цікаво, але на деякий час. З того, що я мав нагоду спостерігати — тут не затримуються наші сімейні пари з дітьми, бо навчання досить дороге та посереднє. Мало звичних розваг: у чотиримільйонному місті нема місцевих філармоній, театрів (але багато кінотеатрів із фільмами англійською). Це, мабуть, єдине, чого мені тут не вистачає. У Гонконгу, який за кілька кілометрів звідси, з культурним життям краще.

Потихеньку думаю про подальші релокації: дуже подобається Азія, зокрема Корея, Японія, Сінгапур; а може — США, Канада, Нова Зеландія, Австралія, поки не визначився остаточно. А от повертатися не тягне взагалі.


За допомогу в підготовці статті дякую Ярославі Тимощук

Эволюция .NET-стека: что изменилось за последние несколько лет

$
0
0

Всем привет. Меня зовут Влад, я старший .NET-разработчик в компании DataArt и около восьми лет работаю с .NET-стеком. В прошлом году написал статью «Как учить .NET»для новичков. В ней были изложены первые и последующие шаги, некая дорожная карта изучения .NET-экосистемы c нуля, однако без упоминаний последних актуальных технологий.

Все более популярным становится .NET Core, новые проекты стартуют на кросс-платформенном ASP.NET Core, в использование входит C# 8. В языке C# остались все фичи предыдущих версий для обратной совместимости, но это не значит, что они рекомендуемые. Эта статья для людей, уже имеющих опыт в коммерческой разработке на .NET-стеке и желающих проапгрейдить знания в связи с последними релизами технологий от Microsoft. А также для тех, кто годами сидит на старых версиях ASP.NET/C# и хочет быть в курсе, что нового в мире .NET-технологий.

В статье я изложу большинство нововведений, ориентируясь на веб-стек.

Open Source .NET

Много лет .NET оставался закрытой системой, однако последние пять лет Microsoft держит курс на открытие исходных кодовсвоих фреймворков. Последнее время ощутимый вклад в развитие ASP.NET Core, F#, VS Code внесло именно комьюнити (судя по количеству пулл-реквестов):

Также некоторые крупные корпорации платят своим специалистам за контрибьютинг в экосистему .NET, например Samsung.

.NET Core

Предпосылками к созданию альтернативной реализации .NET фреймворка стало отсутствие гибкой модульности, портабельности и кросс-платформенности. Рост популярности контейнеризации и повсеместное использование Linux не могли обойти Microsoft стороной, так как одним из плюсов Java перед .NET была как раз возможность хостинга под Linux. Оригинальный .NET Framework был неконкурентоспособен на этом рынке. Также .NET Core — часть open-source-наследия с возможностью для комьюнити влиять на разработку. По-моему мнению, это было абсолютно верным шагом к адаптации технологий от Microsoft к современным тенденциям.

Основное архитектурное отличие .NET Core от .NET Framework — это модульность и портабельность.

Под модульностью я понимаю меньшую связанность компонент и дробление на большее количество пакетов, из которых состоят библиотеки классов .NET (Core FX).Теперь все части приложения являются nuget-пакетами, включая CLR и JIT.

Под портабельностью — возможность поставлять код вместе со средой исполнения нужной версии в одном приложении (Core CLR).

В .NET Core был представлен кросс-платформенный CLI (command line interface) для выполнения типичных задач, управления зависимостями, пакетами, проектом. Например, такой командой мы можем завернуть приложение в единый исполняемый файл, который будет содержать и приложение, и среду выполнения:

dotnet publish -r win-x64 -c Release /p:PublishSingleFile=true

Вот хорошая статьяпро архитектуру .NET Core и способы развертывания.

Предпосылкой для появления .NET Core была платформа DNX, кросс-платформенная реализация .NET Runtime, которая впоследствии стала .NET Core 1. Кстати, dotnet CLI ранее назывался DNX.

Начиная с Visual Studio 2017, уже есть поддержка контейнеризации приложения из коробки.Visual Studio за нас создаст Dockerfile: скачает необходимый образ, запустит приложение. Подробнее можно посмотреть в этом видео. Подробнее о контейнеризации — тут.

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

Все приготовления, описанные выше, можно сделать парой кликов из коробки с помощью GUI Visual Studio.

Что такое .NET Standard

В экосистеме .NET существует восемь реализаций .NET-фреймворка под разные платформы:

  • .NET Core;
  • .NET Framework;
  • Mono;
  • Xamarin.iOS;
  • Xamarin.Mac;
  • Xamarin.Android;
  • Universal Windows Platform;
  • Unity.

.NET Standard — унифицированное версионирование набора поддерживаемых API.

Каждая реализация имеет свою собственную историю релизов и развития. Чтобы понимать, какой пакет может быть совместим и с какой версией, было решено стандартизировать наборы предоставляемых API в спецификации .NET Standard. Для лучшего понимания можно посмотреть интерактивную таблицу.

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

Очень подробно о реальных историях работы и решениях проблем совместимости с .NET Standard вы можете почитать тут.

C# 6, 7, 8

За последние четыре года развития C# (C# 6, C# 7.1, 7.2, 7.3, C# 8) изменения больше всего коснулись механики работы довольно стандартных вещей:

  • методов;
  • ветвлений (любые условные операторы);
  • переменных;
  • классов.

Всего я насчитал 54 новые фичи:

Группировка по количеству новых фич в каждом релизе по сфере изменений

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

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

Упрощенный синтаксис кортежей (C# 7)

Ранее проблема передачи набора значений из метода решалась с помощью паттерна DTO. Однако мы сталкиваемся с тем, что объявление нового типа создает необходимость объявления еще одного класса, что формирует избыточность лишних классов в приложении.

Далее, начиная с .NET 4.0, популярность приобрел обобщенный класс Tuple, позволяющий передавать наборы значений разных типов в одном экземпляре, однако проблема в том, что неудобно работать со свойствами. Для переноса значений в обобщенном классе Tuple элементы кортежа будут называться Item1, Item2 вместо реальных имен свойств.

Проблема была решена в C# 7, что позволило в лаконичной манере передавать наборы значений из методов (кортежи) и объявлять их в качестве переменных, например:

(int, int) GetMinMax(int a, int b)
{
   return (Math.Min(a, b), Math.Max(a, b));
}

      (int min, int max) = GetMinMax(4, 5);

Console.WriteLine(max);
Console.WriteLine(min);

Null-conditional operators (C# 6)

Следующая конструкция позволяет получить имя, если класс person != null. Если же он == null, в переменной first окажется null:

var first = person?.FirstName; 

Очень сильно уменьшает количество кода в случае частых проверок на null.

String interpolation (C# 6)

Синтаксис более удобной шаблонизации строк показан в сочетании со стрелочной (лябмда-синтаксис) функцией (C# 6):

public class UserInfo
{
   public string FirstName { get; set; }
   public string LastName { get; set; }
   public string FullName => $"{FirstName} {LastName}";
}

The nameof expression (C# 6)

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

void NameOfDemo(string lastName)
{
   if (string.IsNullOrEmpty(lastName))
      throw new ArgumentException(
 			message: "Cannot be blank",
 			paramName: nameof(lastName));
}

Local functions (C# 7)

Локальные функции решают следующие две проблемы:

  1. В случае необходимости еще раз использовать кусок кода внутри метода, причем нигде больше в приложении он использоваться не будет.
  2. Ранее мы могли объявить такой кусок кода двумя способами: в виде приватного метода на том же уровне или с помощью анонимного метода внутри.

Анонимные методы применяют механику делегатов. Иногда такой подход уместен для размещения кода, переиспользуемого только внутри этого метода.

С появлением делегатов их синтаксис выглядел так:

 public delegate int DelegateInt(int a, int b);

   DelegateInt func = delegate (int a, int b)
   {
        return a + b;
    };

  var result = func(1, 1);

Затем синтаксис делегатов упростили с помощью лямбд и добавили стандартные делегаты Func & Action:

Func<int,int,int> func = (a, b) => a + b;
var result = func(1, 1);

Сейчас же Visual Studio 2019 предложит вам отрефакторить это выражение до локальной функциис использованием lambda-body-синтаксиса:

int func(int a, int b) => a + b;

var result = func(1, 1);

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

  • Локальную функцию можно определить в конце метода, переменную делегата только перед использованием.
  • В случае рекурсивного вызова для делегата придется заранее определить имя переменной и присвоить пустое (default) значение, чтобы иметь возможность вызывать анонимный метод из него же.
  • Локальная функция не является делегатом и не преобразуется в делегат во время использования, следовательно, не будет потреблять ресурсы в управляемой куче. При работе замыкания локальных функций не будет выделена лишняя память в куче, а будут использоваться структуры. Это поможет снизить потребляемые ресурсы. Если вам важен перфоманс, про реверс-инжинеринг того, как работают анонимные методы в сравнении с локальными функциями, можно почитать тут.
  • В локальных функциях можно использовать синтаксис yield return для создания перечислений, в отличие от лямбда-выражений.

В целом локальные функции и делегаты имеют разные сферы применения. Локальные функции более уместны для выделения заново используемого куска кода внутри конкретного метода.

Readonly members (C# 8)

Новый модификатор доступа readonly для методов позволяет ограничивать их возможность изменять значения переменных. В случае наличия такого кода он не дает скомпилировать проект. Это помогает следить за иммутабельностью структур и классов.

public class DistanceDemo
        {
            public double X { get; set; }
            public double Y { get; set; }
            public double Distance { get; set; }
            public readonly override string ToString() =>
             $"({X}, {Y}) is {Distance} from the origin";
        }

Null-coalescing assignment (C# 8)

Позволяет объединить присвоение значения с проверкой на null. В следующем примере переменной numbers будет присвоено значение, только если она содержит null:

List<int> numbers = null;
numbers ??= new List<int>();

Опять-таки убираем условные конструкции и сокращаем количество кода.

Nullable reference types (C# 8)

Это достаточно значительное нововведение в C# 8. С каждой новой версией C# — разработчики пытаются сделать программирование более лаконичным, предсказуемым и безопасным, помогая с помощью языковых средств избегать возможных типичных ошибок. Зачем делать проверки на null по всему приложению, если можно запретить на уровне компилятора устанавливать null? :)

Новый режим работы можно включить и для проекта в целом, и для какого-то отдельного куска кода, используя специальные директивы. В зоне действия новой фичи все Reference-переменные могут по умолчанию запрещать присваивать себе null, и чтобы иметь возможность присвоить null, нужно будет объявить эту возможность явно, например:

string? Name;

Также был добавлен новый оператор предотвращения вывода warning’ов на потенциально небезопасную операцию.

Этот код создаст warning:

(null as Car).Number

Тут warning не будет:

(null as Car)!.Number

Более детальное описание этой довольной объемной фичи можно найти в этой статье. О том, как Entity Framework Core работает с этой фичей, можно прочитать тут.

Patterns, Patterns, Patterns (C# 7-8)

В последних релизах C# реализовали много фич, которые упрощают условные операторы и делают их более лаконичными.

Кроме pattern matchingиз C# 7, позволяющего включать логику, завязанную на определение типа аргумента в оператор switch/case:

public static int SumPositiveNumbers(IEnumerable<object> sequence)
{
    int sum = 0;
    foreach (var i in sequence)
    {
        switch (i)
        {
            case 0:
                break;
            case IEnumerable<int> childSequence:
            {
                foreach(var item in childSequence)
                    sum += (item > 0) ? item : 0;
                break;
            }
            case int n when n > 0:
                sum += n;
                break;
            case null:
                throw new NullReferenceException("Null found in sequence");
            default:
                throw new InvalidOperationException("Unrecognized type");
        }
    }
    return sum;
}

В C# 8 также появилось много вариаций нового синтаксиса switch-case. Все они интуитивно понятны, так что не буду писать много комментариев:

1. Было:

public static RGBColor FromRainbowClassic(Rainbow colorBand)
{
    switch (colorBand)
    {
        case Rainbow.Red:
            return new RGBColor(0xFF, 0x00, 0x00);
        case Rainbow.Green:
            return new RGBColor(0x00, 0xFF, 0x00);
        case Rainbow.Blue:
            return new RGBColor(0x00, 0x00, 0xFF);
        default:
            throw new ArgumentException(message: "invalid enum value", paramName: nameof(colorBand));
    };
}

Стало:

public static RGBColor FromRainbow(Rainbow colorBand) =>
    colorBand switch
    {
        Rainbow.Red    => new RGBColor(0xFF, 0x00, 0x00),
        Rainbow.Green  => new RGBColor(0x00, 0xFF, 0x00),
        Rainbow.Blue   => new RGBColor(0x00, 0x00, 0xFF),
        _              => throw new ArgumentException(message: "invalid enum value", paramName: nameof(colorBand)),
    };

2. Проверка на совпадение отдельных свойств объекта:

public static decimal ComputeTax(Address location, decimal salePrice) =>
    location switch
    {
        { State: "WA" } => salePrice * 0.06M,
        { State: "MN" } => salePrice * 0.75M,
        { State: "MI" } => salePrice * 0.05M,
        _ => 0M
    };

3. Совпадение нескольких скалярных значений:

public static string RockPaperScissors(string first, string second)
    => (first, second) switch
    {
        ("rock", "paper") => "rock is covered by paper. Paper wins.",
        ("rock", "scissors") => "rock breaks scissors. Rock wins.",
        (_, _) => "tie"
    };

4. Позиционный паттерн на примере кода, который определяет позицию точки в евклидовой системе координат, а именно в каком из 4 квадрантов находится точка:

static Quadrant GetQuadrant(Point point) => point switch
{
    (0, 0) => Quadrant.Origin,
    var (x, y) when x > 0 && y > 0 => Quadrant.One,
    var (x, y) when x < 0 && y > 0 => Quadrant.Two,
    var (x, y) when x < 0 && y < 0 => Quadrant.Three,
    var (x, y) when x > 0 && y < 0 => Quadrant.Four,
    var (_, _) => Quadrant.OnBorder,
    _ => Quadrant.Unknown
};

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

Default interface implementation (C# 8)

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

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

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

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

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

Пример такого решения вы можете найти тут, изучив вначале код из папки starter/customer-relationship, а затем реализацию члена в интерфейсе finished/customer-relationship.

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

Почитать о C# 8 можно на MSDN. Вот ещё неплохая статья на русскомс некоторыми тонкостями использования новых фич.

ASP.NET Core

Если вы работали с ASP.NET MVC/Web API, многие вещи в ASP.NET Core для вас будут в новинку. Фреймворк стал более гибким и более очевидным. Центральное место, которое изменилось, — конфигурирование хостинга и конвейера обработки запросов.

В ASP.NET Core, по аналогии с другими бэкенд-экосистемами, было введено понятие middleware. Его можно определить как:

Функции промежуточной обработки (middleware) — это функции, имеющие доступ к объекту запроса , объекту ответа и к следующей функции промежуточной обработки в цикле «запрос-ответ» приложения. Могут выполнять работу как до перехода на следующую функцию, так и после, в обратном порядке, также решать — нужно ли продоложать цепочку обработки запроса. Подробнее тут.

Концептуальное изображение конвейера обработки запросов

В ASP.NET приложение конфигурируется в основном с помощью Web.Config, и точкой входа в приложение является global.asax.

В ASP.NET Core точка входа в приложение — Program.cs, где конфигурируется, как приложение будет хоститься и с какими параметрами и на каком сервере. В ASP.NET Core хостинг приложения отвязан от самого веб-движка и нет жесткой привязки к IIS, что дает больше гибкости. Также, продолжая концепцию ASP.NET Core с self-contained приложениями, мы можем сделать приложение self-hosted. Также есть опциональный файл Startup.cs, в котором мы пишем код управления жизненным циклом запроса, подключением middleware и регистрацией сервисов во встроенном IoC контейнере. Он опциональный, потому что все это мы можем реализовать и в Program.cs. Еще есть AppSettings.json, который содержит наши параметры приложения в зависимости от выбранной билд-конфигурации.

Конфигурирование проекта стало гораздо более прозрачным. Теперь нужно не искать узлы в огромном XML, а, скорее, писать личный конвейер обработки запросов на более интуитивно-понятном API на C# через конфигурирование кодом.

Теперь MVC и Web API висят на одной шине обработки запроса. Также разработчики позиционируют ASP.NET Core как высокопроизводительный серверный фреймворк, более быструю альтернативу старому ASP.NET.

Кстати, в ASP.NET Core для обработки запросов теперь используется ThreadPool, и нет SynchronizationContext по умолчанию вместо потоков IIS. Такое изменение может привести к серьезным проблемам — дедлокам на продакшене при большой нагрузке, в случае миграции решения с ASP.NET на ASP.NET Core, если у вас имеются специфические варианты реализации работы с тасками и асинхронностью. Подробнее можно почитать тут.

Entity Framework Core

Полностью переписанный Entity Framework Core — альтернатива Entity Framework 6.x, так как имеет почти такое же API, однако есть некоторые возможности, недоступные в EF Core, и наоборот. Полное сравнение можно почитать тут.

Самые значимые отличия в EF и EF Core — отсутствие в EF Core подходов Model First & Database First, использующих файл маппинга концептуальной и реальной модели — EDMX. Опыт показал, что излишние визуальная и концептуальная составляющие делают большие приложения слишком неудобными и медленными для разработки. Нововведением в EF Core стала поддержка нереляционных хранилищ. Посмотреть пример использования можно тут.

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

Я думаю, что, начиная с версии 2.1, в которой наконец-то добавили серверную группировку и еще кучу разных фич, EF Core станет вполне production-ready фреймворком.

Асинхронный паттерн разработки

Сейчас асинхронный паттерн построения приложений становится де-факто стандартом. Асинхронность != многопоточность. Асинхронность — это о том, как эти самые потоки использовать более выгодно, повышая загруженность каждого, тем самым делая наше приложение более экономным, а следовательно, более производительным. Сам паттерн асинхронной работы менялся с выходами новых версий .NET, и уже в .NET 4 принял современный вид.

Начиная с .NET 4, рекомендованный подход — TAP (Task-based asynchronous pattern). В .NET 4.5 / C# 5 он был расширен оператором async/await, позволяющим писать асинхронный код в синхронной манере.

История развития паттернов асинхронного написания кода в C#:

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

Blazor

Клиентский C# в браузере стал реальностью. Не так давно Blazorбыл включен в релиз ASP.NET Core 3, и это своего рода революция в разработке UI-части приложений.

Предпосылки:

  • Asm.js появился в 2013-мкак способ написания высокопроизводительного кода для веб-браузеров. Вскоре поддержка asm.js была включена в Chrome & Mozilla.
  • В конце 2017-гоидея asm.js была развита в релиз Web Assembly (Wasm) года в браузере Chrome. В wasm компилируется код на языках более высокого уровня: C/C++/Rust.
  • Вскоре Microsoft попробовала собрать Mono под платформу wasm. Как результат, получаем реализацию .NET-фреймворка в браузере. Исполняя код, написанный на чистом C#, который работает с IL-инструкциями Mono под wasm-интерфейсом.

Это позволило писать код на C# для виртуальной машины (CLR) в браузере, которая работает под wasm. Фреймворк реализован в паттерне MVVM и немного напоминает компоненты Vue.js, содержащие и разметку и код модели. Также есть возможность вызывать чистый нативный JS.

Так это работает на концептуальном уровне

Технология постепенно развивается. Недавно ко мне пришла вакансия, в описании которой одним из необходимых скиллов был Blazor. Люди делают что-то вроде CAD-системы.

Из интересных пакетов под Blazor можно отметить:

Также рекомендую хорошую статьюс «Хабра» про Blazor.

Туллинг

Все чаще люди переходят на средства разработки от JetBrains. Сам считаю их довольно качественным софтом с хорошим UX:

  • Rider — альтернативная IDE для .NET-стека, имеет родной ReSharper как элемент среды. В Visual Studio при установке ReSharper получаем тормоза за счет того, что, кроме анализа кода в ReShaerper, сама Visual занимается анализом кода, и сам плагин работает через интерфейсы, выставляемые VS, что дает излишнюю работу и двойной расход ресурсов. Еще важный момент. ReSharper работает in process, сама же Visual Studio реализована как приложение x86 (32 bit), что накладывает ограничения на размер потребляемой оперативной памяти на поток, где находится студия.
  • DataGrib — альтернатива MS SQL Management Studio + HuntingDog, имеет встроенный быстрый поиск по объектам баз данных, включая полнотекстовый поиск по хранимым процедурам/функциям, кучу удобств для работы с таблицами, удобный редактор кода с автокомплитом. Отлично подходит под цели Database Developers, однако для администрирования серверов придется использовать либо текстовые команды, либо GUI от нативного MSSMS. DataGrib содержит поддержку кучи драйверов для всех популярных баз данных.
  • Дополнительные средства от JetBrains:
    • dotPeek — альтернатива ILSpy & Reflector;
    • dotMemory — средство для анализа потребляемой памяти, частично повторяет функциональность Performance Monitor и WinDbg, только в более user-friendly манере;
    • dotTrace — перфоманс профайлер;
    • dotCover — средство запуска unit-тестов, поиск проблем с покрытием кода.

Но также довольно неплохо развилась Visual Studio. Последняя версия на сегодняшний день — 2019, имеет более минималистичный интерфейс, были улучшены средства статического анализа кода, рефакторинга, навигации по коду.

Выводы

За последние 4-5лет Microsoft сделала огромную работу по адаптации своих технологий к общим трендам и подходам, давно популярным в остальных технологических стеках. Сейчас все новые проекты, что я знаю, пишутся на стеке ASP.NET Core + EF Core, причем базы данных MS SQL Server и Windows не всегда нужны. По личным ощущениям вполне возможно, что .NET экосистема скоро перестанет ассоциироваться с MS SQL Server. Если это решение на микросервисах, то чаще применяют PostgreSQL или MySQL из-за дешевизны и ненужности заточки под сложную работу с данными или репортинг.

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

Як проводити Design Sprint 2.0, або Створюємо та тестуємо продукт

$
0
0

Я співзасновник і Team Lead в IT-компанії Uptech і Plai, а також сертифікований фасилітатор Design Sprints. Хочу поділитися з вами досвідом, як валідувати свою ідею чи побудувати власну концепцію за допомогою Design Sprint 2.0. Також розповім про те, як ми вирішили підвищити ефективність роботи нашої команди завдяки поліпшеному обміну зворотним зв’язком і як Design Sprint допоміг нам досягти цієї мети. Підсумувавши наші досягнення, ми створили прототип зручного для користувача застосунка й зібрали конструктивні відгуки про нього.

Ця стаття буде корисна як продакт- і проджект-менеджерам, так і дизайн-командам, яким треба швидко провалідувати свої гіпотези.

Design Sprint 5-day Schedule

То що ж таке Design Sprint 2.0

Design Sprint — це 5-деннийпроцес, який дає відповіді на критичні бізнес-питання за допомогою дизайн-мислення, прототипування й тестування різних ідей разом з користувачами. Він призначений для швидкого розв’язання складних завдань, створення нових продуктів або ж удосконалення наявних. Цей процес розробили Джейк Кнапп, Джон Зерацький і Браден Ковіц у Google Ventures та описали в книжці «Спринт». Багато компаній, зокрема Google, Uber, Slack і Lego, використовують Design Sprint для успішного виконання цілої низки завдань.

Design Sprint 2.0 — це оновлений 4-деннийпроцес, розроблений в AJ&Smart, що ґрунтується на оригінальних принципах попередньої версії. У ньому було лише зоптимізовано початковий процес, який тепер триває на день менше. Наша команда пройшла в онлайн-режимімайстер-клас із Design Sprint 2.0, прочитала чи не половину інтернет-ресурсів про спринти й почала їх застосовувати, щоб на власній практиці дізнатися про найефективніші методи впроваджування цього підходу.

Підготовка

Щоб провести Design Sprint ефективно, а не просто повеселитися, треба ретельно підготуватися.

Процес

Передусім вам потрібно знати весь покроковий процес упроваджування цього підходу на кожен із 4 днів.

Модель «Double Diamond»

Загалом весь процес Design Sprint відповідає фазам моделі «Double Diamond», за якою спочатку ми вивчаємо й намагаємося максимально зрозуміти сферу, яку досліджуємо (розфокусування й розгалуження на діаграмі), тоді зосереджуємося на конкретних проблемах (концентруємось і рухаємось до точки фокусування) і больових точках (pain points). Після цього розробляємо різні рішення для подолання вже визначених проблем (розфокусування й розгалуження на діаграмі), тестуємо ці рішення та вибираємо з них найкращі (знову концентруємось і рухаємось до точки фокусування).

День 1 (I частина) — знайомство команди й визначення проблеми: експертні інтерв’ю, заглиблення в проблему та генерування можливостей її розв’язання (через формування запитань HMW і Can We... та постановку Long-Term Goal), а також побудова map того, як функціонує продукт/сервіс (інший об’єкт Design Sprint) і як він взаємодіє з усіма причетними до нього учасниками.

День 1 (II частина) — генерування рішень за допомогою презентації ідей і вправи 4 Part Sketching.

День 2 — презентація рішень, що були підготовані напередодні, голосування й вибір найкращих з них. У другій половині дня — підготовка User Test Flow (схеми того, як користувач застосовує продукт/сервіс) і Storyboard (візуалізація всієї подорожі по продукту/сервісу з достатньою деталізацією та цілковитим узгодженням з тим, що входитиме до прототипу). Пошук користувачів для тестування рішень.

День 3 — прототипування рішення й дальший пошук користувачів для тестування.

День 4 — тестування розроблених рішень з користувачами. Вибір і пріоритетизація незначних змін, які потрібно внести в розроблений прототип на основі фідбеку від користувачів.

Щоб розібратися з усіма деталями процесу, ми спершу прочитали книжку «Спринт», а також використовували докладну інструкцію від AJ&Smart і переглядали їхні відеона YouTube, щоб урахувати всі поради й рекомендації щодо кожної вправи.

Ідея

Важливим складником підготовки є вибір правильної ідеї або проблеми, яку потрібно розглянути під час спринту. Вона має бути досить великою, щоб залучити до цього процесу групу людей, які працюватимуть не покладаючи рук усі 4 дні. Утім не надто глобальною, щоб можна було створити прототип за 1 день.

З новими ідеями в нас ніколи не виникає проблем, ми навіть їх внутрішньо відстежуємо, щоб не забути. Вибираючи ідею, яку треба валідувати, ми провели невеличкий семінар, під час якого проголосували за найцікавіші варіанти для нашої команди. Більшістю голосів було вирішено зосередити наші зусилля на мотивації всіх членів команди залишати один одному регулярні конструктивні відгуки. Ця проблема була для нас дуже актуальною, тож ми вирішили створити такий продукт, який допоможе її розв’язати.

Найкраще Design Sprint застосовувати для розв’язання таких завдань:

  • валідація нових ідей на життєздатність;
  • створення прототипу;
  • перевірка й поліпшення наявних продуктів/сервісів;
  • генерування рішень для складних завдань.

Команда

У Design Sprint узяли участь такі члени команди (на фото зліва направо):

Андрій Бас — фасилітатор (немає на фото);
Діма Коваленко — керівник проекту й бізнес-експерт; ухвалював фінальне рішення під час Design Sprint;
Микола Мельник — дизайнер;
Галина Галкіна — Android-розробник.
Діма Домашенко — дизайнер;
Олег Кривицький — дизайнер.

Команда «Design Sprint»

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

Команда повинна звільнити свій робочий графік на весь період Design Sprint, щоб мати змогу на 100% зосередитись на проблемі, а не відволікатися на інші завдання.

Інструменти

Перед спринтом потрібно добре підготуватися. Забронювати кімнату для переговорів, підготувати блокноти для нотаток, наліпки, крапки для голосування, дошки, папір, фломастери, перекуси під час перерв... Весь список можна переглянути тут.

День 0. Перед Design Sprint

Перед спринтом ми провели дослідження ринку щодо наших конкурентів (як-от 15Five, Lattice, Culture Amp тощо) та підготували список плюсів і мінусів уже наявних рішень. Усі рішення конкурентів перевірили самостійно. Завдяки такій підготовці ми добре ознайомилися із цією сферою й були готові кинути їй виклик.

День 1. Визначте виклики й підготуйте рішення

Мета 1-годня — з’ясувати, з яким саме викликом ми стикаємося, і створити кілька шляхів його розв’язання. Для цього провели інтерв’ю з експертами та сформували запитання «Як ми могли б...» (How Might We...).

Почали з інтерв’ю експертів. Оскільки ми самі мали бути користувачами рішення, яке збирались розробити, то вирішили зосередитись на власній компанії. Як експертів — людей, які працюють у цій галузі й розуміють проблему — запросили наших членів команди. Для правильного проведення такого інтерв’ю ми послуговувалися посібником групи Nielsen Norman.

Поки експерти розмовляли, решта команди записувала запитання «Як ми могли б...» (HMW). Мета цієї вправи — записати всі ключові проблеми, які турбують експертів. Після категоризації та голосування звузили низку наявних запитань HMW до найважливіших, серед яких:

  • Як ми могли б зменшити власні зусилля й ділитися зворотним зв’язком на місці за менш як 10 секунд?
  • Як ми могли б побороти страх і дискомфорт, щоб поділитися зворотним зв’язком?
  • Як ми могли б заохочувати культуру зворотного зв’язку?

Запитання HMW

Довгострокова мета

Після мозкового штурму й голосування треба виокремити 2-річнумету. У нас вона звучала так: «Кожна взаємодія в команді включає зворотний зв’язок».

Для вимірювання мети ми обумовили:

  • 10% членів команди щодня беруть участь у надаванні зворотного зв’язку;
  • від взаємодії до зворотного зв’язку проходить менш як 1 день.

Sprint questions

Запитання «Спринту» схожі на запитання HMW, але вони мають конкретніший характер і формуються для команди Design Sprint передусім з метою допомогти визначити перешкоди, які можуть завадити в досягненні поставленої мети.

Нашими Sprint questions були:

  • Чи можемо ми усунути емоційні й психологічні бар’єри в колективі?
  • Чи можна зробити обмін зворотним зв’язком простішим, для того щоб члени команди мали змогу поділитися ним якнайшвидше?
  • Чи можемо ми розробити модель такої взаємодії, яка дала б змогу щодня збирати відгуки 10% членів команди?

Робота над Sprint questions

Map

Ми намалювали map, щоб допомогти візуалізувати user flow і мати повне уявлення про таку взаємодію. Розділили його на кілька важливих етапів: взаємодію, обмін зворотним зв’язком (де користувач долає важливий психологічний бар’єр), підготовка та обмін відгуком (або його одержання).

Як цільову сферу вибрали сам процес обміну зворотним зв’язком, щоб зосередитися на розробленні відповідного інструмента протягом наступних днів. Ми припустили, що культурний і психологічний бар’єри вже подолані, оскільки в противному разі нам потрібен новий спринт для проходження цього етапу.

Map

Lightning Demos

Після обідньої перерви ми провели Lightning Demos. Мета цієї вправи — сформувати множину ідей, які згодом використовуватиме колектив для створення своїх рішень. Відомо, що чи не кожна нова ідея — це лише свіже поєднання кількох уже наявних. Щоб надихнутися й зібрати різноманітні рішення/ідеї, команда провела чудові дослідження та продемонструвала приклади того, як працюють інші продукти: monobank, Uber, сповіщення в iPhone, Google Calendar, Lattice, 15Five, Auchan тощо.

Кожен з названих продуктів містить цікаві рішення й ідеї, які можна імплементувати в нашому кейсі.

Команда працює над Lightning Demos

Скетч

Перший день закінчується генеруванням рішень, яке проводиться в 4 етапи. Перші три з них — Note-taking, Doodling і Crazy 8s — це вправи на візуальну розминку. Четвертий етап — це скетчинг, під час якого члени команди придумали власні концепції для нашого продукту, щоб згодом вибрати найкращий варіант для подальшого прототипування. Кожне з таких рішень складалося з 3 основних кроків, мало влучну назву й деталізоване пояснення. Не варто забувати про анонімність усіх концепцій, щоб наступного дня під час голосування члени команди не були упереджені у своєму виборі.

Концепція

Висновки першого дня

  1. Перший день спринту є найважчим і найбільш насиченим як за кількістю різноманітних вправ, так і за фокусом, якого вони вимагають. Упродовж цього дня ухвалюється багато важливих рішень, тож саме тоді задається тон усіх наступних днів. Тому дуже важливо приходити на спринт підготованим!
  2. Деякі труднощі в команди викликало розуміння Sprint questions і HMW questions. Здавалося, що це ті самі запитання. Згодом ми дослідили, що Sprint questions формуються радше в негативному контексті для роботи з перешкодами, а HMW questions ставляться натомість у позитивному контексті й націлені на досягнення мети (а не роботу з перешкодами). Ось розміщене хороше відеопоясненнявідмінності між цими двома видами запитань.
  3. Під час роботи з map дуже легко заглибитись у деталі та змарнувати забагато часу. Мета map — дати загальне розуміння шляху користувача, тому ставте часові ліміти на всі вправи й строго їх дотримуйтеся. Не забувайте, що Design Sprint — це швидка валідація й рух уперед, а не знаходження «ідеального» рішення.
  4. Після кожного дня важливо збирати команду і ще раз наголошувати, що було зроблено й чим можна пишатися, а також ділитися одне з одним зворотним зв’язком. Це підтримує мотивацію й допомагає оперативно розв’язувати проблеми (якщо вони є) та підвищувати ефективність роботи команди.

День 2. Проголосуйте за рішення і створіть розкадрування

Мета другого дня — вибрати рішення, яке команда має протестувати, і заповнити всі необхідні для цього деталі на Storyboard.

Презентація рішення

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

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

Наприкінці презентації кожної концепції фасилітаторові було б добре запитати: «Хто-небудь розмістив крапку в цій концепції щодо ідеї чи функції, про які я не згадував?» І справді, кілька разів виявлялося, що члени команди помітили пропущені фасилітатором ідеї. Такий підхід дає змогу всім членам команди бути на одній хвилі.

Презентація концепцій

Голосування

Опісля голосування на опитуванні «Straw Poll» (синхронному голосуванні) команда зробила свої ставки за найкращу концепцію. Усі четверо учасників команди проголосували за одну й ту саму ідею. Пізніше керівник проекту також вибрав саме ту концепцію.

Ключова ідея цієї простої та добре продуманої концепції полягала в тому, щоб почати зі сповіщень, які спонукатимуть користувачів давати відгуки. Застосунок пропонує кожному користувачеві певну групу, яка складається із членів його команди та з якою він може поділитися зворотним зв’язком. У такому разі користувач повинен провести по фотокартці іншого члена команди пальцем уліво, щоб пропустити, або вправо, щоб надати зворотний зв’язок (так само, як у Tinder). Щоб залишити відгук, треба пройти кілька дуже простих кроків. До того ж до системи можна було додати власні фільтри, щоб вона могла автоматично підлаштовуватися під потреби команди.

Найкраща ідея

User Test Flow

Під час вправи User Test Flow кожен член команди прописав 6 кроків, які користувачі програми повинні виконати з моменту входження в неї й аж до результативного обміну відгуками. Ми представили одержані User Test Flow і проголосували за найкращий варіант. Цей крок призначений для того, щоб полегшити й пришвидшити наступний етап.

User Test Flow

Storyboard

Найважливішим завданням цього дня було заповнити Storyboard. Ідея полягає в тому, щоб промальовувати весь шлях користувача через наш продукт з достатньою деталізацією, щоб стало цілком зрозуміло, що ж саме входить у прототип. Після Storyboard уже не має виникати жодних запитань щодо концепції, для того щоб протягом усього наступного дня команда могла зосередитись лише на прототипуванні, а не знову повертатись до її обговорення. На цьому етапі не варто також додавати абсолютно нові ідеї, хоч це іноді й важко.

Згодом ми розмістили кроки користувача у 8 комірках і почали малювати екрани концепції. Вирішили повторно намалювати їх, щоб оновити кілька кроків. Загалом на Storyboard у нас пішло 3 години, під час яких ми також обдумували різні рішення та підходи. Наприкінці цього дня у нас виявилось аж 11 екранів (замість очікуваних 8), але деякі з них були доволі простими.

Storyboard

Висновки другого дня

  1. Може здатися, що вправа з оглядом усіх концепцій дещо показова. І хоч усі роботи презентуються як анонімні, та завдяки почерку, особистому стилю тощо майже всі члени команди розуміють, де чий концепт. Проте дистанціювання концепту від особистості, тобто без прив’язки до автора, все-таки дає змогу краще сфокусуватися на самій роботі. Це допомагає найкращим ідеям вигравати, що дуже важливо для результативності самого Design Sprint.
  2. Вправа User Test Flow є насправді допоміжною перед вправою Storyboard. Вона допомагає краще сфокусувати команду навколо шляху користувача в майбутньому концепті. Ця вправа показує, що навіть після двох днів роботи над концепцією у членів команди можуть бути різні бачення.
  3. Під час Storyboard важливо уникати групового прийняття рішень чи надиктовування тексту, адже в такому разі можна очікувати лише посереднього результату. Найефективніше — розділити завдання між людьми або невеликими командами, а потім об’єднати результати їхньої роботи. Важливо також підготувати весь супутній текст/контент для прототипування наступного дня.

День 3. Прототип

Мета цього дня — побудувати прототип, який зможе підтвердити наше рішення.

Паперовий прототип

Паперовий прототип — це найпростіший із можливих підходів для тестування майже будь-якої програми. Розділивши роботу між усіма членами команди, за 1 годину мали вже готовий прототип. Завдяки такій злагодженій роботі ми відчули єдність колективу і радість від досягнення мети. Цей прототип протестували двічі: між членами команди Design Sprint (Андрієм і Миколою) та з одним зовнішнім користувачем (Оленою). Таке швидке тестування додало всім упевненості перед наступним днем.

Під час тестування склали список майбутніх удосконалень для паперового прототипу й тих місць, де в користувачів виникала плутанина. Такі вдосконалення помітили на стікерах, які розмістили на Storyboard у відповідних частинах. Після цього проголосували за 4 найважливіші зауваження й удосконалили Storyboard на основі вибраних пропозицій.

Паперовий прототип

Цифровий прототип

Оскільки в нас уже були детальний Storyboard, паперовий прототип і 3 дизайнери в команді, працюючи одночасно, приблизно лише за одну годину ми створили на Figma цифровий прототип.

Цифровий прототип

Пошук і графік інтерв’ю з користувачами

Протягом цього дня Діма Д. працював над плануванням і сценарієм інтерв’ю користувачів. Оскільки наш застосунок розроблявся для внутрішнього використання, було вирішено запросити 4 членів нашої команди та 2 зовнішніх експертів. Для останніх розмістили відповідний пост у Facebook. Дуже багато користувачів зголосилися допомогти в тестуванні, тож ми розпланували дзвінки. Незважаючи на те, що рекомендується провести 5 інтерв’ю, вирішили провести їх 6, щоб одержати цінну інформацію від більшої кількості людей.

Висновки третього дня

  1. Етап паперового прототипу дає дуже хорошу мотивацію й значно поліпшує його лише за якусь годину-дві. Хоча цього етапу й немає в офіційній версії Design Sprint, ми таки рекомендуємо його проводити.
  2. У команді важливо мати дизайнера, який відповідатиме за розроблення цифрового прототипу. Без такої людини результат вийде розмитим.
  3. Підготовку до інтерв’ю й набір тестерів краще почати ще напередодні. Якщо вам треба знайти користувачів з інших міст/країн, для цього добре підходять реклама у Facebook або Craigslist. Перед самим інтерв’ю важливо мати весь його сценарій і попрактикувати співбесіду один з одним для виявлення слабких місць.

День 4. Тестування прототипу з користувачами

Мета четвертого дня — провести тестування прототипу, одержати зворотний зв’язок і прийняти рішення про наступні кроки щодо продукту.

Інтерв’ю

Щоб розробити остаточні тести й ретельно підготуватися до співбесіди, ми розпочали роботу на годину раніше. Намалювали на дошці велику таблицю з іменами всіх інтерв’юєрів і переліком функціоналу, про які хотіли одержати відгук. Перше інтерв’ю було заплановане на 11-туранку. Воно дуже допомогло нам вникнути в цей процес і налаштувати правильний відеозапис. Після таких інтерв’ю вам може знадобитися близько 2 годин для виправлення виявлених вад і вдосконалення обраного підходу або прототипу.

Інтерв’ю з користувачем

Що стосується самого процесу інтерв’ю, то ми трохи відкоригували його під наші потреби. Наприклад, замість того щоб одна людина робила миттєві нотатки й записувала плюси/мінуси чи важливі нюанси під час інтерв’ю, до цього долучилась уся команда. Після інтерв’ю ми об’єднали наші замітки, щоб створити комбіновані стікери плюсів і мінусів. На червоних стікерах записували пункти для поліпшення прототипу, на зелених — те, що користувачам сподобалося, а на синіх — загальні коментарі. Це дало нам можливість переконатися, що всі ідеї були записані, а сама команда мислить в одному напрямі.

Результати тестування прототипу

Висновки четвертого дня

  1. Брати інтерв’ю в користувача доволі важко. І 6 інтерв’ю на день — це надто багато, тому 5 було б таки достатньо.
  2. Важливо залучати до проведення інтерв’ю всіх членів команди (а не тільки дизайнерів чи продуктових менеджерів). Розробники, біз-деви чи інші експерти також зможуть це робити ефективно після короткого інструктажу більш досвідчених колег і за наявності скрипту. Це дасть усій команді змогу краще усвідомити цінність спілкування з користувачами й мотивуватиме робити це й надалі. Такі вправи формують емпатію і розвивають продуктовий підхід.
  3. Перед тестуванням самого прототипу важливо встановити хороший контакт із користувачем, знайти з ним спільні теми, трохи пожартувати тощо. Наприклад, коли ми одержуємо підтвердження, що дзвінок можна записати, то інколи жартуємо, що «викладемо цей дзвінок на YouTube і надішлемо співбесіднику публічне посилання». Клієнти сміються, адже розуміють, що це неможливо.
  4. Після 4-годня спринту команда буде дуже втомлена. Це чудовий час, щоб розрядити обстановку і відсвяткувати результат. Подякуйте всій команді за хорошу продуктивну роботу. Вони точно це заслужили!

5 день. Після Design Sprint

Наступного тижня після Design Sprint ми згрупували відгуки користувачів у категорії/загальні фітчі. Потім за допомогою крапок проголосували за найважливіші зміни, які потрібно внести (за червоні чи сині стікери).

Опісля сформували 14 фітчів, які можна було реалізувати. Спершу намалювали шкалу впливу й зусиль і розмістили картки у відповідному масштабі — залежно від того, наскільки вони складні для виконання та як вплинуть на програму. Наприкінці обвели групу карток у верхньому лівому куті (найменше зусиль, найбільший вплив).

Це ті функції, на яких ми зупинимося в наступному оновленні цього застосунка. Команда вирішила перевірити ще кілька ідей і повернутися до вибраної концепції за кілька місяців до того, як перейти до самого розроблення програми.

Команда Design Sprint

Підсумок

Нашій команді сподобався процес Design Sprint. Багато продуктових команд розповідає про Design Thinking та інші модні теми, але мало хто замислюється над тим, як застосувати їх до реальних проектів. Design Sprint — це дуже практичний і покроковий процес застосування принципів Design Thinking для розв’язання складних завдань і підготовки нових продуктів. Такий підхід створює належну атмосферу в колективі для розвитку творчого мислення у розв’язанні проблем. Ми задоволені цим підходом до розроблення продукту та його кінцевим результатом.

Якщо вашій команді потрібно подолати серйозні виклики, то Design Sprint може стати правильним вибором! Уперше його проводити може бути складно, але з допомогою ресурсів, зазначених знизу, та хорошої підготовки навіть команда, яка ніколи раніше не мала досвіду з Design Sprint, зможе провести його ефективно.

Корисні посилання:

Інтеграційні платформи (iPaaS): у чому фішка

$
0
0

Привіт, мене звуть Ярослав, і в цій статті я розповім про Integration Platforms (iPaaS), що використовують у різних проектах у компанії SoftServe і робочих практиках System Integration Engineer.

Дефініції та історична довідка

Що ж таке Data integration? Основна функція Data integration ― маршрутизація даних з розрізнених джерел (баз даних, систем, мобільних/веб-застосунків через API тощо) в єдину систему/платформу в межах організації або організацій. Наступне завдання — узгодження різних типів даних з єдиним форматом, що використовуватимуть під час будь-яких маніпуляцій, трансформацій або синхронізації.

Інтеграційний напрям виник унаслідок різкого збільшення протягом останніх 10 років кількості різноманітних систем, що застосовують усередині організацій (Human Capital Management, Portals, Apps тощо), та, відповідно, потреби у швидкій синхронізації даних між цими системами. До певного часу інтеграційну функцію виконувало ESB, основною метою якого було, по суті, забезпечити транзакційний контроль, перетворення даних і маніпуляції з ними. Згодом інтеграційні завдання стали складнішими й комплекснішими, а отже, вони потребують окремих підходів, специфічних навичок і тулсету.

До ESBПісля ESB

Від ESB до iPaaS

Згодом підхід ESB еволюціонував, і почали з’являтися так звані iPaaS-рішення. Беручи на себе основні функції low-level-девелопменту і спростивши або навіть цілком перебравши на себе підходи до security, data encryption/decryption, data transformation, deployment-процедури, інтеграційні платформи дали змогу розробникам інвестувати суттєво більше часу саме в інженерний підхід.

Основні відмінності між ESB та iPaaS

Загалом підхід ESB виник унаслідок потреби в керуванні даними в локальних або застарілих legacy-системах. Історично ESB-патерн виник ще задовго до хмарних систем/інтеграцій, він підходить для координації та роботи з даними, але, на мій погляд, цьому підходу бракує гнучкості.

Інтеграційні платформи (iPaaS) створено для підтримання хмарних рішень/систем. Теоретично iPaaS можуть зменшити або усунути потребу в локальних серверах чи апаратному забезпеченні, також iPaaS розроблено для керування інтеграцією з хмарних застосунків.

Масштабованість (Scalability)

ESB-підхід можна використовувати для вертикального масштабування в наявному архітектурному рішенні. Зокрема, iPaaS більше підходить для горизонтального масштабування (наприклад, додавання до наявного середовища нових підсистем/систем або компонент).

Наступна і, на мій погляд, основна відмінність — Multitenancy-підхід. Тут саме iPaaS має очевидну перевагу, адже є змога надавати доступ у реальному часі до даних, що зберігають у різних системах або застосунках. ESB-підхід менш адаптований до виконання завдань такої складності.

Окрім ESB-підходу, інтеграційні платформи зробили величезний крок щодо підтримки мікросервісної архітектури. Наприклад, більшість платформ (Dell Boomi, MuleSoftтощо) містять і підтримують:

  • API Management;
  • Discoverability;
  • Serialization;
  • Dynamic Process Routing;
  • Support for Docker;
  • Support for External Load Balancers;
  • Support for Custom Authentication;
  • Support for industry protocols for inter-process communication;
  • Platform APIs.

Також однією з основних переваг інтеграційних платформ є використання так званих InBox Connectors ― готових рішень для забезпечення зв’язку/з’єднання з різноманітними системами. Наприклад, якщо розглядати таку платформу як Dell Boomi, можна побачити, що вони покрили своїми «конекторами» більшість відомих і можливих платформ.

Завдяки Data Mapping інтеграційні платформи також дають змогу швидко трансформувати дані з одного формату в інший (можна опрацьовувати відповідну структуру даних через профайли).

Dell Boomi Data Mapping

MuleSoft DataWeave

Підсумовуючи все зазначене вище, скажу таке: інтеграційні платформи створили низку можливостей, за допомогою яких можна побудувати доволі комплексні рішення з мінімальними часовими затратами. Це дуже актуально, враховуючи складність інтеграційних завдань, з якими ми починаємо стикатися на практиці. Для прикладу, одним з найбільших проектів, в якому мені доводилося працювати, була побудова системи, що синхронізувала дані між enterprise-системою (величезною мережею супермаркетів у США) та чотирма різними HCM-системами. У такому разі синхронізація відбувалася за допомогою eventdriven near-realtime-підходу, тобто інтеграційне рішення реагувало на події в різних системах, що в результаті тригерили інтеграцію і синхронізували дані.

Огляд найліпших інтеграційних платформ

Більшість наявних тепер інтеграційних платформ підтримують ключові інтеграційні й комунікаційні патерни, зокрема application-to-application, publisher-subscriber, real-time and event-driven web services, streaming, batch, ETL.

Однією з перших на ринку з’явилася Informatica, що й досі лишається однією з найстабільніших платформ. Першість зробила її лідером, однак щодо гнучкості, підходів до розробки й інтерфейсу, на мій погляд, наступні два інструменти цікавіші.

На другому місці за популярністю, згідно з Gartner Magic Quadrant for Enterprise Integration Platform as a Service, — платформа Dell Boomi.

Dell Boomi дає змогу побудувати складне інтеграційне рішення за порівняно короткий проміжок часу. Наприклад, я прораховував часові затрати на мінімальний проект, що з’єднував SuccessFactorsі HCM-систему одного з клієнтів (ураховуючи тестування, моніторинг, деплоймент-процедуру). Затрати на ESB-based-прототип, написаний на Java + Deployment, займав приблизно в 10 разів більше часу й утричі більше ресурсів, ніж такий самий прототип, побудований за допомогою Dell Boomi iPaaS-рішення. Звичайно, використання платформ, попри переваги у швидкості розробки, має й певні негативні наслідки, зокрема під час розробки доведеться враховувати наявні дефекти платформ, downtime й оновлення. Однак сукупні переваги все ж суттєво більші.

Ще одна популярна платформа, з якою мені доводилося працювати в межах низки проектів, ― MuleSoft. Її основна перевага — доволі звичне для Java-розробника середовище, а також можливість працювати з інтеграційними рішеннями на нижчому рівні.

З чого розпочинати роботу з інтеграційними платформами

Відверто кажучи, для мене як для Java-розробника перейти від класичної розробки до інтеграційних рішень було досить важким кроком. Спочатку ресурсів, які розкривали б усі можливості платформ, було не так уже й багато, а ті, що мені вдалося знайти, були не з дешевих. Та завдяки менеджменту компанії я таки дістав змогу поспілкуватися з розробниками з Dell й набути відповідні знання і сертифікації.

Наступна проблема — те, що функція System Integration Engineer порівняно нішева й не дуже поширена на українському ринку розробників. Як наслідок ― відсутність у більшості компаній кар’єрного зростання спеціалістів цього напряму; труднощі з пошуком кваліфікованих спеціалістів на ринку й навчальних рішень для них. Попри те, що використання iPaaS-рішень доволі популярна практика в США та, наприклад, Австралії, знайти розробників на ринку України досить важке й комплексне завдання, яке ми з колегами розв’язали за допомогою побудови різноманітних курсів у SoftServe ІТ Academy, ramp-up-програм у межах компанії. Сукупно це все перетворилося на цілу окрему гілку в компанії зі своїми підрівнями й кар’єрним зростанням у System Integration Engineer.

Якщо взяти позицію Intermediate System Integration Engineer у компанії, базовий технічний стек потрібних знань матиме приблизно такий вигляд:

  • базові знання Java/.NET/Python;
  • базові знання JavaScript/Groovy;
  • розуміння структури REST/SOAP Web Services;
  • базові знання DB Desing/Quering;
  • базові знання Security Principals (Security Mechanisms, Authentication Types, Characteristics of Application Security);
  • Cloud Services (Compute Engines, Monitoring Services, Load Balancing, Auto Scaling тощо);
  • Data Interchange Formats (JSON/XMLStructure, Parsers, XSLT);
  • IntegrationPlatform (Proficient knowledge of Integration Platform configuration management principles, working with Batch and Near Real time data processing, design complex processes including, working with licenses, complex data mapping, base introduction into Integration Platforms sripting. This capability level also includes proficient
  • knowledge of implementing web services connectors using REST or SOAP principles, connecting and working with DBdata. Error and Exception handling. Working with revisions);
  • сертифікації (тут залежить від конкретно вибраної платформи).

Найвища кар’єрна ланка ― Expert System Integration Engineer ― архітектурна позиція, що передбачає поглиблені знання у переліченому вище стеку й експертні/поглиблені знання в кількох інтеграційних платформах.

Якщо вас цікавить напрям системних інтеграцій і ви вирішили копнути глибше, нижче пропоную кілька корисних посилань.

Інформація про платформи:

Огляд патернів і підходів до розробки:

DOU Ревізор у Вінниці: «Офіс LetyShops у торговельному центрі з кальянами на даху»

$
0
0

Цього разу DOU Ревізорзавітав до вінницького офісу LetyShops — кешбек-сервісу, що допомагає понад 9,5 мільйонам користувачів повернути частину грошей за онлайн-покупки.

LetyShops працює з 2014-гороку та має українських засновників. У компанії по одному офісу у Києві та Вінниці, а також представництво в Угорщині. Головний офіс — вінницький, у ньому працює 160 співробітників, серед яких 25 розробників та 15 технічних спеціалістів. Глобально в команді LetyShops 230 осіб.

В околицях і поблизу

Уже чотири роки офіс LetyShops знаходиться за адресою проспект Юності, 35А у будівлі колишнього торговельного центру. Поруч розміщені ринок, торговий центр, а також школа та університет.

LetyShops займає всі три поверхи та дах колишнього ТЦ. Загальна площа офісу складає 2472 м2: по 800 м² кожен поверх та 72 м2 — тераса на даху. Кількість квадратних метрів на особу в робочому приміщенні становить 14,9 м2 (відповідно до даних компанії).





Поблизу офісу — розмаїття закладів, де можна не надто дорого пообідати: кафе «Бібліотека», «Теремок», McDonald’s і ресторани «Терамаре» та Steak House. Середня вартість обіду в закладах складає 70 грн.

Також комплексний обід можна замовити в офіс-менеджера компанії. Щопонеділка до офісу надходить меню, за яким до кінця робочого тижня працівники можуть зробити замовлення на наступний. Вартість подібного обіду: перша страва — 20 грн, друга — 60 грн, а комплекс коштуватиме 70 грн.

Спеціалісти можуть придбати продукти у «Фуршеті» або «Сільпо» та приготувати їжу на одній з кухонь LetyShops (див. розділ «Офісний побут»).

Паркувальний майданчик для автівок та мотоциклів безкоштовний і вміщує 12-15 авто.Проте з паркуванням є певні складнощі, оскільки офіс розташовано біля торгового центру та невеликого ринку із побутовими товарами.




Для велосипедистів виділили місця всередині офісу. Всього на мініпарковці можна розмістити вісім «залізних коней». За безпекою вуличного та внутрішнього паркувальних майданчиків стежить офісний охоронець.

Офісний побут

Офіс відчинено 24/7, проте щоб потрапити всередину після 21:00, спеціалісти мають повідомити про свій прихід у чаті. Картка-перепустка потрібна лише для того, щоб відчинити головні двері. Пересуватися між поверхами та кімнатами можна вільно і без неї.





Залишити верхній одяг та речі можна у гардеробній на першому поверсі. Кожен співробітник має власну шухлядку. Там є дзеркало та спеціальний бокс, куди можна здати батарейки.

У дизайні офісу LetyShops панує принцип «користь має бути в усьому», навіть у наліпках на стінах, що не дають врізатися у скляні двері.

Наразі для офісу припинили замовляти одноразові стаканчики, втім поки ще використовують залишки з останньої партії. Сортування в LetyShops впровадили півроку тому, баки для вторсировини розмістили на кухнях. Щоб наочно показати, які види сміття сортуються, а які — ні, над контейнерами повісили декупаж з популярних офісних відходів. Це допомогло розібратися із базовими правилами, однак подекуди стаканчики все одно помилково потрапляють до контейнерів з пластиком. Тож відходи потім досортовує підрядник, що вивозить вторсировину.

На кожному поверсі є кухні, де завжди можна знайти свіжі фрукти, солодощі, сухі сніданки, каші, спеції. Тут є холодильники, мікрохвильовки і навіть плити з каструлями та сковорідками. На другому поверсі додали ще й духовку, каструлі та блендер. Найбільшу кухню з великими столами розміщено на першому поверсі. Будували її спеціально для того, щоб спеціалісти могли посидіти за великим столом та поспілкуватися з колегами.






Будівлю зводили під торгові площі, тож опенспейс не передбачався. Тому команда знесла стіни між колонами та на другому поверсі змінила дифузори на круглі зі стандартних решіток. Про минуле життя в якості ТЦ в офісі нагадують ескалатори, проте ними не користуються.

У будівлі тоноване скло, тож з вулиці не видно, що відбувається всередині. Спеціалісти LetyShops розповіли, що вікна затонували після того, як одна з команд позавішувала їх ковдрами від надмірного світла. Нині думки спеціалістів розділилися: комусь світла вистачає, а комусь затемно. (див. розділ «DOU Ревізор запитує»).

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

На кожному поверсі є окремі чоловічі та жіночі вбиральні, а от душова кімната — лише на першому. Там можна знайти мило, гелі і рушники. Співробітники також можуть попросити офісних «хазяюшок» випрати речі та закинути їх на сушку. Це досить зручно для тих, хто приїздить на велосипедах. Можна доїхати, забігти у душ, а наприкінці робочого дня забрати вже випрану форму.






Робочий простір

Звичайний графік роботи в LetyShops — з 8:00-10:00 ідо 17:00-19:00.Але його можна коригувати, головне — попередити команду. Якщо співробітник затримався на роботі після дев’ятої вечора влітку або після восьмої вечора взимку, компанія компенсує йому поїздку на таксі. Для цього необхідно записати витрачену суму в чатботі. Регулярно послугою користується команда Сustomer Care департаменту, що працює на зміні з 14:00 до 23:00.





Чітко визначеної опції віддаленої роботи в компанії немає. Працювати вдома можна, але у форматі точкових домовленостей зі своєю командою та ментором.

Під робочі опенспейс-зони виділили другий та третій поверхи. На першому розмістили зони для розваг та відпочинку, а також переговорні кімнати.

Усього в офісі дев’ять переговорних кімнат: чотири на першому поверсі, по дві на другому та третьому, а також одна мобільна переговорна кімната. Це невеликий пересувний будиночок для зустрічей «сам на сам».






Забронювати мітинг-рум можна через Google-календар. Раніше в компанії писали розклад зустрічей на дошках біля кожної переговорної кімнати, проте від такої ідеї відмовилися і надали перевагу бронюванню через календар. Щоб популяризувати такий спосіб, для корпоративного телебачення зняли окреме відео про те, як користуватися бронюванням.

Щоб розмови по мобільному не заважали команді в умовах опенспейсу, офісом розмістили червоні будки для дзвінків.

Занепокоєння в співробітників викликають «пусті» зони. Команда активно зростає, тож поки в офісі існують пусті безлюдні зони, де нічого немає. Співробітники воліли б використовувати простір з користю, навіть якщо туди наразі ніхто не в’їхав.

Першого робочого дня співробітнику видають капці для офісу та виділяють шафку у гардеробний кімнаті. До welcome-pack’у входять сумка, футболка, чашка, блокнот, олівці та стікери.

За новачком закріплюються два ментори: один — відповідальний за професійне зростання, а інший — за соціалізацію. Стандартно на процес адаптації виділяють два місяці. У цей період для новачків проводять загальну екскурсію офісом та серію індивідуальних зустрічей, де розповідають про цінності, традиції та визначають цілі на випробувальний термін. При цьому новому співробітникові доступні всі переваги роботу в компанії, крім компенсації за відвідування спортивної зали. Опція відкривається вже після завершення випробувального терміну. Медичне страхування співробітникам компанія не надає.

Також для нових членів команди раз на квартал проводять «уроки бізнесу», де засновники компанії розповідають про складові бізнес-моделі LetyShops.

Спеціалісти можуть висловити побажання щодо робочої техніки, обрати ноутбук або настільний комп’ютер конкретної моделі. Ноутбуків та стаціонарних комп’ютерів у компанії приблизно порівну. Дизайнери надають перевагу MacBook’ам, розробники на десктопі зазвичай працюють за ПК з 16-32гігабайтами оперативної пам’яті та SSD на 256 гігабайтів. Ноутбуки у розробників зазвичай Lenovo 14″ ThinkPad/IdeaPad, Dell 13"-15″ XPS або MacBook 13 Pro. Команда Customer Care працює на стандартних стаціонарних комп’ютерах.






Побажанням спеціалістів щодо техніки, меблів, профільної літератури займається відділ People Care — «команда піклування за співробітниками». Можуть надати новий стіл, стілець, комп’ютер, підставку під ноги тощо. Та найчастіше надходить запит на вологі серветки (усі люблять чисте робоче місце). Замовлення виконують оперативно, додаткове узгодження потрібне лише для того, щоб придбати нову техніку або, наприклад, книгу за 2-3тисячі гривень.

Робочі місця в LetyShops — сидячі. За бажання стоячи можна попрацювати на кухні першого поверху. Проте опція недоступна спеціалістам, що працюють за стаціонарними комп‘ютерами.

Внутрішня структура компанії побудована у вигляді системи крос-функціональних команд, кожна з яких має свій фокус та цілі. За кожною командою закріплений свій HR, що допомагає учасникам адаптуватися, покращує комунікацію всередині команд, сприяє професійному зростанню співробітників.

«Крос-функціональність» полягає у тому, що розробники можуть пропонувати ідеї для маркетингу, а UX-дизайнери — допомогти з аналітикою. В LetyShops заохочують не лише розвиватися у своїй сфері, а й вивчати суміжні компетенції. Тож компанія має багато варіантів внутрішнього навчання.

Коли виникає запит від колег, розробники можуть допомогти підтягнути навички. Так, двічі на тиждень один з Front-end-розробників LetyShops проводив курс з HTML/CSS-верстки, Android-розробник — курс з SQL-аналітики, а продуктові менеджери — майстер-класи з Customer Development.

Також у LetyShops проводять ініціативи з розвитку soft-навичок: уроки з фасилітації, майстер-класи з надання зворотного зв’язку та правил усвідомленого спілкування. Раз на місяць усі команди обирають представників, що презентують результати роботи на загальному для всіх офісів Demo. Раз на два місяці в кожному місті проходить окрема зустріч усіх команд під назвою Team Talks, де CEO компанії розповідає про стан бізнес-економіки, стратегію та відповідає на питання команд.

Відпочинок і натхнення

У LetyShops є кілька лаунж-зон: маленькі, у коридорах другого та третього поверху, та велика біля кухні першого. Оскільки на першому поверсі з робочих зон лише ізольовані мітинг-руми, на лаунж-зону не накладають жодних обмежень, що стосуються галасу.




Тут команда часто збирається на перегляд футбольних матчів, фільмів та гру на PlayStation 4. В одній з шухляд знайшли Sega, однак з’ясувати під час ревізії, чи вона працює — не вдалося.




Також відпочити можна на даху, де для спеціалістів облаштували терасу із зоною для куріння сигарет та окремо — кальянів. Основне правило — погасити за собою кальян, коли йдеш з даху. Холодної пори року зону для куріння кальянів обшивають прозорою захисною плівкою від вітру та встановлюють обігрівач UFO. Теплої пори дах використовують як майданчик для роботи та гриль-вечірок. Також на терасі знаходиться турнік, та він перестав користуватися популярністю через те, що багато хто користується зоною саме як місцем для куріння.






У LetyShops є масажна кімната, куди двічі на тиждень приходить масажист. Зручний час можна обрати та забронювати через офіс-менеджера. Якщо надходить багато запитів, масажиста запрошують частіше. Обмежень на кількість сеансів немає, компанія компенсує половину витрат на масажі.

Компанія має футбольну команду, яка раз на тиждень збирається на тренування у залі, що орендує для них LetyShops. У планах команди — проводити тренування хоча б двічі на тиждень та взяти участь у ІТ-лізі, що відбудеться взимку у Вінниці.

Недивно, що найбільшою популярністю серед переваг офісу користується спортивно-ігрова кімната. У ній можна знайти стіл для настільного тенісу, кікеру та спортивне приладдя: грушу, бруси, шведську стінку, хула-хупи, дошку Євмінова, гантелі. Спеціалісти компанії розповіли, що найзатребуваніший у кімнаті — стіл для настільного тенісу. На другому місці — спортивне приладдя для тренування, а от настільний футбол значної популярності в LetyShops не здобув.





Музичні таланти можна реалізувати у відповідній звукоізольованій кімнаті, або ж «шум-румі». Більшість інструментів — місцеві, проте співробітники часто приносять та залишають і власні інструменти. Наразі у кімнаті є клавішні, барабанна установка та гітари. На базі «шум-руму» LetyShops вдалося зібрати власний невеликий гурт.





У офісі виділили невеличку зону для загортання подарунків на честь дня народження співробітника та його дітей, весілля, першої річниці роботи в компанії, дня святого Миколая. Подарунки — індивідуальні, тож комусь дістаються бобки (худі), декому — сертифікати до Rozetka, MakeUp чи інших популярних магазинів. Пакуванням подарунків займаються офіс-менеджери.

У компанії проводять уроки англійської та іспанської мов. Кожна група має по два півторагодинні заняття на тиждень. До англійської мови додали ще іспанську, оскільки компанія з-поміж інших ринків працює з Іспанією та країнами Латинської Америки.

Щомісяця проходять неформальні тимбілдинги для команд, а двічі на рік — зимовий та літній корпоративи за межами міста. Дні народження компанії святкували поїздками до Одеси, Єгипту та Карпат.

А ще є корпоративне телебачення, завдяки якому члени команд можуть дізнатися більше про власних колег, новини від інших команд та стан справ на ринку. В співробітників беруть інтерв’ю, де вони розповідають про своє життя, хобі та уподобання. Випуски виходять раз на тиждень на YouTube-каналі компанії та доступні за посиланням виключно для команди LetyShops.

DOU Ревізор запитує

Ми провели анонімне опитування вінницької команди LetyShops, у якому взяли участь 77 співробітників, серед яких 25 технічних спеціалістів. Ми поцікавилися, чи задоволені вони офісом, і попросили оцінити за п’ятибальною шкалою певні характеристики, серед яких розташування, графік роботи і офісний простір.

Найнижчу середню оцінку у 3,86 бала отримала можливість працювати віддалено. Трохи краще оцінили паркування та роботу вентиляції — 25% спеціалістів поставили характеристикам три та менше балів. Спеціалісти коментують, що дуже важливо покращити регулювання температури в офісі, зокрема зробити більш плавне охолодження теплої пори року та посилити опалення в зимовий період.

За результатами опитування, найбільше команді LetyShops до смаку розташування офісу. Почесне друге місце посіли простори для відпочинку: 82% оцінили їх на тверду п’ятірку, а решта — на 4 бали. Також схвальні відгуки отримав дах, лаунж-зони, спортзала та музична кімната. Хоча кімнату для куріння кальянів деякі спеціалісти воліли б перенести на зимовий період всередину офісу. Серед інших ідей команди щодо покращення офісу — влаштувати розвозку та дитячу кімнату.

Також ми особисто поцікавились у спеціалістів компанії, як же їм живеться, і поставили два нескладних запитання: що найбільше подобається в офісі та що хотілося б поліпшити або змінити.

Макс, Back-End Developer, 2,5 роки в компанії

Робоче місце цілком забезпечує компанія: столи, робочу техніку, всю начинку комп’ютера. Можна попросити як ноут, так і стаціонарний комп’ютер, монітор чи навіть два. Мені зручніше працювати з двома Dell-моніторами за стаціонарним комп’ютером з SSD на 256 GB. Мені цього з головою вистачає.

Вінниця — не надто велике місто, тож я з іншого кінця добираюся десь за 30 хвилин. Транспорт не завантажений, і без заторів я легко і спокійно доїжджаю до роботи.

У компанії є культура гри в настільний теніс — дуже багато тих, хто грає. Груші та спортивне приладдя поруч не заважають, та особисто я ще ніколи не бачив, аби ним хтось користувався.

У нас не поширена практика віддаленої роботи, але хотілося б. Вже цю тему піднімали, і компанія рухається в цьому напрямку. Хоча графік дуже зручний, завжди можна за потреби відійти під час робочого дня, бо у нас все ґрунтується на довірі.

Наталя, Front-End Developer, 2 роки в компанії

Мені подобається, що в нас опенспейс, ми не сидимо по кабінетиках, тож немає закритості. Щоб перепочити, можемо з командою піти на дах або пограти в настільний теніс. До речі, грати я навчилася саме в LetyShops.

Я працюю над акціями, і часто трапляється, що залишаємося до ночі. Наприклад, коли наближається чорна п’ятниця чи щось подібне. Коли дуже багато роботи, то можна попрацювати з дому.

Деколи трапляється, що вентиляція працює дуже голосно. Шум від неї досить фоновий, його не помічаєш, та коли припиняє гудіти, одразу відчуваєш полегшення. Подекуди в офісі досить дме, тож багато хто кутається у ковдри. Та ми справляємось — підкручуємо вентиляцію або вдягаємо щось тепліше, і все ок.

Ярослав, Front-End Developer, 2 роки в компанії

Класно, що в нас з’явилося велопаркування. Можна не турбуватися, що велосипед поцуплять чи він змокне під дощем на вулиці. Звісно, трапляється, що приїжджає багато народу, і тоді місця не вистачає. В такі дні велосипеди ставлять поруч зі стійкою, та вони нікому не заважають. Поки що всі вміщаються.

Сергій, Senior Android Developer, 2 роки в компанії

Мені подобається, що у нас є турнік та спортивне спорядження. Раніше я користувався турніком на терасі, та там люди курять, тож зручніше займатися у спортзалі внизу. Там дуже зручно і покачатися, і в теніс пограти.

У нас в принципі складно знайти паркування, бо поруч будівельний ринок, і весь ряд завжди забито. Це не надто велика проблема — зараз такі паркувальні майданчики всюди. Хотілося б покращити вентиляцію, бо мене кілька разів протягнуло. Я двічі змінив місце і нарешті знайшов, де не дме.

Класно, що в нас немає овертаймів. За два роки я лише двічі чи тричі лишався овертаймити, і то максимум на три години. Робота нормально планується, і не треба весь час гасити якісь пожежі.


Ну що, ми рушаємо далі... А якщо ви хочете, щоб DOU Ревізор завітав до вас, пишіть: revisor@dou.ua

Ми катаємося по Україні в пошуках найкреативніших та нестандартних офісів ІТ-компаній. Разом з нами ви зможете зазирнути за лаштунки офісного життя. Але вирішувати, гарний це офіс чи ні, будете тільки ви!

Стежте за нами у Facebook.

Підписуйтесь на відеоканал DOU Ревізора на YouTube.


Фотограф: Леся Коверега

Как выбрать правильные метрики для продукта

$
0
0

Меня зовут Анна Костюк, и несколько месяцев назад я стала Product Manager в Dev-Pro после 7 лет в бизнес-анализе. Решения, которые мы принимали с командой на основе метрик, позволили успешно реализовать несколько новых фич для нашего клиента, что привело к доверительным и надежным отношениям с ним. Я хочу рассказать, как уйти от разработки приложения вслепую и не попасть в ситуацию, когда никто не пользуется фичей, на которую ушли месяцы разработки.

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

Будущее за data-informed-разработкой, хотя не все проекты могут похвастаться, что уже следуют этой тенденции. Слово «метрика» звучит сейчас повсюду: люди измеряют эффективность команд, процессов, продуктов, бизнеса в целом. В контексте продакт-менеджмента продуктовая метрика — это показатель, который позволяет оценить успешность фичи.

Ментальная модель метрик

Наш продукт — это ящик. На входе мы имеем потребности пользователей и задачу, которую пытаемся решить, на выходе — решенную проблему пользователя. Но это не черный ящик! Чтобы достичь своих целей, пользователи выполняют определенные действия. Если мы измеряем их во времени, они становятся продуктовыми метриками.

Почему продуктовые метрики важны

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

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

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

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

Это не панацея. У метрик есть недостатки и ограничения

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

Если ваши метрики падают или не достигают уровня, который вы запланировали, важно понять, почему это происходит. Ваша фича решает проблему, которая не важна пользователю? Или она решает эту проблему, но в недостаточной степени? Она решает эту проблему хорошо, но пользователи недовольны производительностью, UX? Тут наступает время детального анализа. Но чтобы иметь достаточно информации для его проведения, необходимо установить хорошую метрику.

Что такое хорошая метрика

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

Хорошая метрика равно понятная метрика. Мы сейчас можем отслеживать фактически все что угодно. Очень легко провалиться в бездну метрик и сабметрик и упустить из виду главную цель. Каждый раз, когда вам приходит в голову посчитать количество Android-пользователей, которые запросили демо в среду перед Днем благодарения, остановитесь и подумайте, что вам дает эта цифра. Как она поможет вам и команде принимать решения? Об этих данных, скорее всего, никто не вспомнит после первого озвучивания.

Метрики должны быть сравнимыми. Вы должны иметь возможность сопоставить их с другими действиями пользователя в системе. Либо с этими показателями в предыдущем периоде.

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

Фреймворки метрик

Их много, но один из самых распространенных — Google’s HEART Framework:

  • Happiness — достаточно абстрактная категория, потому что измерять счастье очень сложно. Одна из метрик — NPS score. Всем знакомый экспресс-опрос, предлагающий оценить по шкале от 1 до 10 вероятность того, что вы порекомендуете приложение коллеге. Или рейтинги приложения в аппсторах — тоже показатель счастья. Это достаточно высокоуровневая метрика — общий уровень удовлетворенности клиента вашим продуктом.
  • Engagement — категория метрик, которая показывает, насколько глубоко и активно пользователи взаимодействуют с вашим продуктом. Замеряем количество отправленных сообщений, созданных постов или, например, каналов в Slack. Падающие метрики из этой категории должны служить сигналом, что «здоровье» продукта ухудшается и нужно принимать меры.
  • Adoption — имеет значение на этапе релизов новых версий или фич. Здесь вы измеряете, сколько пользователей использовали новый функционал, обновили приложение до последней версии и т. д. Низкий adoption может быть обусловлен одним из двух факторов — неэффективным продакт-маркетингом или проблемами с discoverability новой фичи.
  • Retention — важно не только, чтобы юзеры попробовали новую фичу (кликнули ее из любопытства), необходимо понимать, сколько человек продолжают ее использовать спустя две недели, месяц, квартал.
  • Task Success — насколько легко пользователи достигают цели, пользуясь вашими фичами: какова успешность поиска, количество ошибок и т. д.

Важно! Метрики индивидуальны для вашего продукта, понадобятся не все категории. Например, у вас B2B-проект, в котором пользователи обязаны выполнять определенные действия изо дня в день, потому что это их работа. В таком случае нет смысла измерять Engagement — они вынуждены! Более показательной будет метрика Task Success — насколько просто они могут выполнять свои задачи.

Как же выбрать метрики, которые подходят именно для вашего продукта

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

Чтобы правильно определить подходящие для вас метрики, задайте себе вопросы: какая цель фичи? кто будет ею пользоваться?

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

Метрики важно сопоставить с «путешествием» пользователя — customer journey. Полезным станет измерить, сколько пользователей проявили намерение воспользоваться фичей (например, сделали необходимый сетап в персональных настройках или перешли на новую страницу), сколько из них непосредственно воспользовались фичей для достижения конечной цели, сколько человек продолжает активно использовать фичу, достигнув цели.

Case study

Я работаю над успешной платформой для sales-менеджеров. Мы с командой создавали фичу, которая позволила в рамках этого продукта назначать встречи с потенциальными клиентами. Всем знакома ситуация, когда предлагаешь созвон: «Давай в понедельник в 14:00», а в ответ получаешь: «Нет, давай во вторник в 15:00». А дальше: «Нет, вторник мне не подходит, давай в среду» — и так до бесконечности, пока клиент не «отвалится». Наша цель — решить эту проблему. Фича заключалась в том, что sales-менеджер открывает свой календарь, просматривает график, выбирает несколько опций и вставляет их в письмо. Потенциальный клиент получает это письмо, видит тайм-слоты и может одним кликом назначить встречу. После клика по ссылке создается событие в календаре.

Какие метрики мы выбрали после релиза:

  • Вовлеченность — сколько раз пользователи вставляли свои тайм-слоты в письма. Сколько пользователей открывали календарь, но не выполняли финальное действие — вставку тайм-слотов в письмо.
  • Усредненная метрика — сколько раз в неделю в среднем один человек использует эту фичу.

На этапе запуска фичи мы измеряли adoption — сколько уникальных пользователей пришло в течение месяца и квартала после релиза. Так как у нас B2B-продукт, нам было важно понимать не только количество пользователей, но и число уникальных команд, которые начали пользоваться новым функционалом.

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

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

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

Здорово, мы получили много-много цифр! Но как мы применяли их в разработке — вот главный вопрос. Как они повлияли на продуктовые решения, которые мы принимали?

Во-первых, функциональность была доступна в двух местах: в самом приложении и в расширении для gmail — числа драматически отличались. Проблема заключалась в UI: доступ в приложении к фиче был сложнее. Чтобы повысить adoption, мы улучшили UI так, чтобы люди легче находили опцию и, следовательно, стали активнее ее использовать.

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

В-третьих, мы имели данные, сколько в среднем раз sales-менеджеры вставляют тайм-слоты и сколько дней предлагают для встречи с клиентами. Казалось бы, какая нам разница-то? 7, 10 или 15? Пусть делают, что хотят, — мы дали им фичу. Но, имея эту информацию, мы убедились: наш UI максимально удобный, все опции видны и отображаются наилучшим образом, так что человеку, который выбирает подходящий слот, не приходится скроллить и переключаться.

В-четвертых, подтверждение гипотезы. Мы проверили основную гипотезу, что, если sales-менеджер предлагает выбранные тайм-слоты клиентам сразу, а не втягивает их в долгие дискуссии о подходящей дате, получаем более высокую конверсию встреч. Но мы все-таки заметили, что результат мог быть еще лучше: не все клиенты понимали, что они могут сделать это в один клик; они не понимали, что получили ссылки. Мы дали UI более похожий на кнопку — и стали еще успешнее достигать цели.

И в-пятых, открытие. Когда мы разрабатывали фичу, то не знали, что сложится ситуация, когда на момент отправки все временные опции доступны, а через несколько часов уже неактуальны и забронированы. С помощью аналитики мы поняли, что так терялось в среднем 3% потенциальных встреч, и добавили функционал, чтобы дать клиенту возможность запросить новое время.

Выводы

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

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


Оптимальне Delivery: від ZIP-архіву до docker push

$
0
0

Привіт, мене звуть Олександр Нагірняк. Я співпрацюю з EPAM Ukraine як Lead Software Engineer. У цій статті я розповім вам про те, як зробити процес Delivery трохи зручнішим і безпечнішим для вашого психічного здоров’я. Усі поради в цьому тексті ґрунтуються на власному досвіді, набутому в процесі спроб, помилок і боротьби з дедлайнами. Буду радий продовжити обговорення в коментарях до матеріалу.

Disclaimer: усі персонажі й описані події вигадані. Будь-який збіг з реальними людьми чи подіями — випадковість. Англіцизми використано задля точнішого формулювання конкретних ситуацій.

Рутина

Розробка, доставка коду та його розгортання в середовищах ускладнюються з року в рік. Напевно, кожен з нас — розробників — хоч раз у житті писав свій монолітний застосунок і далі виконував рутинні операції: спаковував його до ZIP-архіву, завантажував на FTP, заходив через RDP на веб-сервер, звантажував підготовлений архів на 400 МБ, робив бекап поточної версії в папку на кшталт BackupMyBuglessApp, копіював нову версію, виконував IIS recycle pool чи TomCat restart.sh. Ну а далі, за класикою жанру, щось відмовлялося працювати, ви розуміли, що просто забули докинути іншу версію одного bin-файлу, і процедура розгортання повторювалася знову (а іноді — і не раз).

Якщо таких ситуацій з вами ніколи не траплялося, то я радий за вас. Розгортання — це не та процедура, за якою хочеться пускати сумну ностальгійну сльозу.

Звичайно, такий алгоритм дій не може бути прийнятним. Особливо, коли команда починає розширюватися, система зростає семимильними кроками, а функціональність зі спринта в спринт збільшується в рази. Тоді згодиться Shell або PowerShell (дякую, Білле!). Залишається лише обрати людину з команди, яка б зайнялася написанням коду на цих прекрасних інструментах. Переконаний, що таку людину ви знайдете без проблем. Найімовірніше, це будете ви.

Тривожні дзвіночки перших проблем

Минає тиждень кропіткої праці й пошуку на Stack Overflow інформації про те, як написати for-цикл у PowerShell. Скрипти написано, усі щасливі від того, що подвійне натискання лівої кнопки миші розгортає нову версію веб-застосунку. Уже фантазуєте про «велику червону кнопку», натискання якої запускатиме той самий процес.

Ваша команда працює злагоджено й професійно, у вас з’являється дедалі більше обов’язків, і ви вирішуєте делегувати процес розгортання і, наприклад, узяти один день відпустки, щоб трішки відпочити від написання коду. Здавалося б, нічого не віщує біди, поки на пошті чи будь-якому іншому месенджері, не з’явиться повідомлення від колеги з якоюсь подібною помилкою:

File C:\GreatestApp\Staging_MyBuglessApplication\scripts\copyAndDeploy.ps1 cannot be loaded because the execution of scripts is disabled on this system. Please see «get- help about_signing» for more details.

На щастя, перший рівень помилок розв’язують через елементарний пошук. Але не все буває так просто, бо розгорнуто версію з критичною проблемою і потрібно швидко зробити rollback. Але, на жаль, файлу rollbackEverythingBad.ps1 ще немає, бо завдання на цю функціональність загубилося десь разом з низькопріоритетними бізнес-завданнями. Тож наступний робочий тиждень витрачаємо на написання такої функціональності, а також налаштування CI/CD-інструменту запуску тестів і використання скриптів, які були написані до цього.

Ну а потім усе закручується, стрімко летить час, і вже за рік система містить декілька (десятків)сервісів, які спілкуються через брокер-повідомлення, логи зберігаються десь у NoSQL-базі, на кшталт Elastic, налаштована система сповіщень і система горизонтального скейлінгу під кожний сервіс.

Усі проблеми стають дедалі масштабнішими. Особливо ті, що пов’язані з протягуванням тих самих bin-артефактів через середовища веб-серверів dev ->QA ->staging ->prepreprod ->preprod ->prod ->demoEnvironmentForInvestors. До того ж ще й зі zero-downtime-розгортанням, оскільки це важливо з погляду бізнесу.

Інший тип проблем — проблеми з використанням інфраструктурних сервісів, незалежно від того, розгорнуті вони у хмарному провайдері чи у власних серверах. А «вишенькою на торті» серед незручностей стає розгортання абсолютно нового середовища з усією інфраструктурою (бази даних, брокери повідомлень, load balancers тощо) десь у Середній Азії, бо замовник визначив стратегічною ціллю запустити бізнес у цьому регіоні й саме тепер.

Досвідчений оператор ПКпрограміст, читаючи між рядків, уже виокремив ключові проблеми розгортання коду в середовищах. Але все ж наведу їх у списку:

  • неправильне розуміння правильних потреб проекту в певний проміжок часу;
  • заплутаність процесу з протягування артефактів крізь середовища й rollback невдалих версій сервісів;
  • складність моніторингу логів, горизонтального скейлінгу сервісів і zero-downtime-розгортання тощо;
  • об’ємність налаштування абсолютно нового середовища, наприклад в іншому регіоні.

Докладно про проблеми розгортання і як їх розв’язати, з огляду на мій досвід, і йтиметься далі.

Розгортаємося правильно

Маючи чітке розуміння, що ми не плануємо завоювати весь світ, варто вибрати технології й інструменти успішного доставляння веб-застосунку в середовище й розгортання надалі. Незалежно від того, розробляємо ми проект середньої ентерпрайзності, чи стартап з наступними раундами інвестицій, чи робимо редизайн наявної системи.

Щоб уникнути холіварів, я не прив’язуватимуся до конкретної мови програмування. Лише наголошу, що вона може бути будь-яка, якщо: а) вона розв’язує проблему ефективно, б) легко знайдуться люди, які нею не лише пишуть, а й роблять це професійно. В іншому разі є ймовірність ніколи не вийти в продакшен.

Також я не розповідатиму вибір того чи іншого інструменту з Continuous Integration, Continuous Deployment. Майже всі сучасні CI/CD-інструменти можуть забезпечити зручну доставку коду в середовище як вбудовані функції або плагіни. Jenkins, TeamCity, GitLab CI — це все справа смаку, розміру проекту й кількості грошей у гаманці.

Тож ідемо далі. Чи не основний інструмент, що допомагає перемогти головні проблеми доставки коду й розгортання, — це Docker.

По-перше, він дає змогу уніфікувати ваші бінарники в один артефакт. Наприклад, у вас є один сервіс, написаний на Python. Він називається Ping. Цей сервіс робить HTTP-запит на інший сервіс під назвою Pong, написаний на .NET Core, і є тести, написані на Node.js, які перевіряють кінцевий результат. За допомогою Docker, незалежно від платформи, у результаті ви отримуєте готовий артефакт ― Docker Image, а згодом запущений Docker Container.

По-друге, можна задати цьому артефакту конкретну версію (тегнути), на кшталт users-2019.9.13, і завантажити на публічний або приватний Docker Registry: Docker Hub, AWS Elastic Container Registry тощо. Це дає змогу розв’язати проблему версій і надалі спрощує rollback, якщо той знадобиться.

У результаті ви можете взяти раніше завантажений артефакт і протягти його крізь усі потрібні середовища dev ->QA ->staging ->prod та запускати всюди, де встановлено Docker і/або є той чи інший Docker Orchestrator.

Звісно, у Docker Image є недоліки. Наприклад, у цей артефакт не можна успішно втиснути застосунок, який написано під .NET Framework 4.5 (двічі дякую, Білле!).

А ще один величезний недолік — те, що постійно забуваєш, як писати мапінги портіву docker-compose.yml (host port: container port vs container port: host port) :)

Отож створили ми той артефакт, поклали мегабайти в якесь своє приватне сховище.

Що далі

Далі потрібно цей артефакт десь розгорнути, наприклад, у тому чи іншому хмарному провайдері. Звісно, можна це зробити на власних серверах, але доведеться підтримувати всю інфраструктуру, враховуючи рівень операційної системи (а можливо, і заліза). Тому, якщо в продукту немає ніяких обмежень з розгортання в клауді, то варто брати клауд.

Розгортання на on-premise — це тема окремої статті, я не експерт з розгортання на власних машинах, тому не думаю, що маю право про це писати, хоча концепції досить схожі. Досвіду з розгортання на AWS у мене трохи більше, тому всі наступні приклади будуть орієнтовані саме на цю платформу. А тему розгортання під Azure залишимо євангелістам Microsoft.

Гаразд, маємо Docker Image з нашими бінарниками всередині. Маємо AWS-акаунт, API key / API secret уже видано. Можемо впевнено почати розгортання артефакту. Нині для цього є такі опції:

  1. Підняти власну Virtual Machine aka AWS EC2 instance, налаштувати систему логування, установити Docker, написати скрипти, що стягуватимуть Docker Image і запускатимуть з нього Docker Container.
  2. Використати одну з опцій готового сервісу AWS Elastic Container Service aka ECS:
    • ECS EC2 mode — автоматизація того, що написано в пункті вище.
    • ECS Fargate mode — абстракція того, де розгортають контейнер, якщо EC2 mode підіймає фізичну VM-куу вас в AWS-акаунті, то Fargate запускає контейнери «десь».
  3. Використати AWS Elastic Kubernetes Service aka EKS — адаптований під AWS Kubernetes.

Вибір тієї чи іншої опції залежить від поставленого перед вами завдання. Якщо потрібно запуститися швидко й без DevOps-команди, попри те, як там усе працює «під капотом», можна взяти ECS. Коли потрібно такий собі «швейцарський ніж» з детальнішою системою моніторингу і є команда досвідчених інженерів інфраструктури, то правильнішим вибором був би EKS.

Звісно, таке різноманіття підходів і технологій було не завжди.

Від теорії до практики: ECS й Infrastructure as a Code

Тоді, коли стартував мій поточний проект в компанії, були доступні опції 1 і 2.1, тому логічно було піти шляхом більшої автоматизації. Ба більше, однією з вимог до продукту було zero downtime deployment, а ECS давав змогу легко це налаштувати.

Клауд має одну хитру особливість: дуже легко «підсісти» на його сервіси, особливо, коли потрібно зробити MVP за півроку й часу на вибір того чи іншого інструменту обмаль. І якщо розгортання на AWS ECS для пет-проекту чи стартапу на AWS Free Tier з декількома сервісами можна описати якось так, то розгортання 46 сервісів повноцінного продукту буде трошки важчим завданням. Особливо з налаштованими сповіщеннями про ліміти CPU, RAM тощо на кожний сервіс і конфігурацією сервісу (ConnectionStrings, ServiceAddress), яка зберігається в AWS Parameter Store. А також з купою сервісів самого AWS, зокрема:

  1. Application Load Balancer — задля збалансування вхідних реквестів і балансування взаємодії між сервісами (так, у нас деякі сервіси спілкуються через HTTP і проблеми з цим виникають украй рідко, але вони все ж є, про це нижче).
  2. Simple Queue Service / Simple Notification Service — це ті сервіси, що мають стрімкі підвищення навантаження (спайки), спілкуються через брокер повідомлень.
  3. ElastiCache — кешування за допомогою Redis.
  4. Elasticsearch — full-text search у логах і в активностях користувача.

І це все на (1)dev -> (2)QA -> (3)staging ->(4)prod чотирьох середовищах.

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

Згодиться підхід Infrastructure as a Code, тобто декларування в коді всього того, щоб ви хотіли бачити в себе в AWS-акаунті в кожному з середовищ. Навіть якщо буде потреба розгорнути середовище з усіма сервісами в новому регіоні.

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

Також варто сказати про доцільність використання цих інструментів: якщо ви розумієте, що проект стрімко розвиватиметься до кількох десятків сервісів та кількох десятків людей, і це все відбуватиметься в багатьох середовищах, то, на мою думку, варто інвестувати час у цей підхід.

Якщо це буде проект на 5-7 людей,а стрімке зростання передбачено лише після Series B, то витрачати час на цей підхід не варто. Тобто потрібно впіймати «правильну» мить, коли слід упроваджувати цей підхід.

Завжди є куди рухатися

Прогрес не стоїть на місці, наш проект також, тому завжди є миті, коли треба поліпшити систему. Аби тільки час на це був. Перше глобальне поліпшення доставки коду — це перенесення наших сервісів з ECS EC2 mode на ECS Fargate mode. Тож після суттєвого зниженняцін на Fargate і через те, що в першому сценарії все ж таки треба реліз від релізу заходити на EC2 Instance й оновлювати машинки security-патчами, а з Fargate це робити не треба, бо за нас це робить AWS — вирішено переїхати на Fargate.

Інший і чи не основний момент, який потрібно вдосконалювати з погляду delivery ― це залежність між сервісами. Зі збільшенням кількості сервісів не завжди зрозуміло, який сервіс залежить від якого, а це головний біль і витрачений час для людини, що виконує розгортання. Тому вирішено розділити сервіси на три типи:

  1. Back-end for Front-end (BFF) — такий собі API Gateway, що має implicitflowаутентифікації і збирає потрібні дані для конкретної сторінки з внутрішніх сервісів (у внутрішньому networking AWS) уже за допомогою server-to-serverвзаємодії.
  2. Platform Services — сервіси, які мають значну різницю в бізнес-процесах.
  3. Core Services — спільні сервіси, що використовують для зберігання користувачів, відправлення нотифікацій, запису активностей тощо.

Тобто BFF може зробити HTTP-реквест на продуктовий сервіс або на Core Service, продуктовий сервіс може зробити HTTP-реквест на Core Service, або зробити PublishMessage, але не навпаки. Продуктові сервіси не можуть спілкуватися між собою, Core Services не можуть напряму спілкуватися між собою, якщо потрібно перекинутися кількома кілобайтами даних, то один Core Service також надсилає message в SNS topic.

Отже, зв’язаність між сервісами стає дедалі нижчою, і можна побудувати логічний порядок розгортання сервісів.

Висновки. Куди ж без них?

Найголовніше з погляду доставки коду та його розгортання надалі — це розуміння стадії продукту, його масштабів і перспектив розвитку. Навіть якщо взяти всі найліпші технології і практики, але водночас не мати чіткого розуміння, куди рухатися, то дорога з delivery-продукту може завести в невідомість.

Протягування артефактів крізь середовища, версіювання артефактів, rollback невдалих версій — усе це вирішувати значно приємніше з докером, ніж без нього. Запуск застосунків у кількох екземплярах, моніторинг логів, налаштування zero-downtime-розгортання швидше робити на клауді, тому ж самому AWS, ніж на власних серверах. А керувати інфраструктурою ефективніше за допомогою підходу Infrastructure as a Code.

Звісно, це все можна робити без вищезгаданих інструментів, завжди можна архівувати бінарники й розгортати вручну. 2007-го жбуло так класно.

Сподіваюся, вам було цікаво прочитати про мій досвід, а поради, надані в матеріалі, стануть у пригоді. Якщо хочете подискутувати, поставити запитання або ж розповісти про власний досвід розгортання ― чекатиму вас у коментарях.

Як ми розробили Android-застосунок і втратили все, крім досвіду

$
0
0

Привіт! Мене звати Сергій, я Director of Technology в Onix-Systems. Компанію засновано 2000 року, вона займається аутсорсингом для організацій зі США та Європи. За останні 8 років у нас було чимало різних проектів у різних напрямах. Часто в команді були люди, які, попрацювавши деякий час, говорили, що в аутсорсі нецікаво, а треба йти в стартапи й створювати свої продукти. Ми провели експеримент зі створення свого продукту всередині компанії, і я хотів би поділитися досвідом з тими, хто впевнений, що працювати над продуктом краще, ніж на замовлення.

Ми розробили YouStream― мобільний застосунок з можливістю транслювання відео/аудіо з камер телефона й рестриму з екшен-камери. Проте виникла проблема через застосування іконки, схожої на ту, яку використовує YouTube. А потім ми й взагалі втратили доступ до облікового запису, під яким було створено проект у Google Cloud Platform для YouStream. Докладніше про це — нижче.

Експеримент проводили люди, які перебували на бенчі, для того щоб розвинути й продемонструвати нашу обізнаність у вузькій ніші. За весь час в проекті взяв участь один Android-розробник та один дизайнер. Пізніше у відділі маркетингу створили сайтпроекту. Ми розраховували, що коли зможемо показати власний застосунок, до нас почнуть звертатися серйозні замовники, кому буде потрібен такий досвід, і ми можемо розробити для них потужніші рішення на базі нашої бібліотеки. Поки що до нас приходять студенти, які шукають рішення для своїх дипломних робіт і просять про допомогу.

Ідея

Колись у 2017 році в аутсорсингу часто траплялося завдання розробити мобільний застосунок, з можливістю транслювання відео/аудіо з фронтальної та головної камер Android-пристроїв + рестриму з екшен-камери типу GoPro й китайських аналогів.

Проаналізувавши готові бібліотеки й рішення на ринку, ми не знайшли жодного готового рішення, яке б задовольняло всі наші вимоги. Сирі, забаговані, написані на одному коліні бібліотеки, абсолютно не готові для продакшену. Деякі з них були платними, але працювали гірше за opensource-рішення. Основні проблеми наявних розробок були такі:

  • жодна не вміє працювати з екшен-камерами;
  • нестабільність;
  • незрозумілі витрати пам’яті;
  • відсутність типу масштабу для фрагмента попереднього перегляду;
  • керування пропорціями прев’ю камери;
  • відсутність логіки з перепід’єднаннями в разі втрати зв’язку;
  • погана робота за умов нестабільного з’єднання;
  • неправильне генерування часових міток для аудіо (на YouTube такий стрим має звукові артефакти);
  • повільна робота на будь-яких пристроях;
  • проблеми з ліцензіями тощо.

Реалізація

Після всього цього ми ухвалили рішення спробувати розробити свій велосипед. Розробляли бібліотеку декілька місяців. Увесь конвеєр кодування RTMP-протоколу зреалізовано в нативному модулі, написаному мовою C для максимальної продуктивності. Найскладніше було змусити коректно працювати одночасно два мережеві модулі для рестриму з екшен-камери, довелося дивитися підказки в соурскоді андроїду.

У результаті ми одержали aag-бібліотеку AVLib + демо + кастом-ліцензію, яка дає змогу використовувати продукт з комерційною метою без відкритого коду.

Технічні моменти, які хотілося б виокремити

Щоб одержати рестрим «прев’ю камери» з екшен-камери на RTMP-сервер, довелося патчити libRTMP і розв’язувати проблему одночасної роботи з двома мережевими інтерфейсами. Основною проблемою було змусити працювати libRTMP із сокетом, який під’єднано до вибраного мережевого з’єднання, яке створено на боці Java. У цій ситуації сокет, який створено на боці Java для визначеного мережевого з’єднання, що ми передавали в хащі libRTMP, через різний час закривала сама система, і ми не зрозуміли такої поведінки.

Довго ламаючи голову над цією проблемою, ми підійшли до неї з іншого боку: з’єдналися із сокетом, створеним усередині libRTMP через fwmarkd на задане мережеве з’єднання. Можливо, комусь це знадобиться.

Java side:

Network cellularNetwork;
// request and obtain network
// ....
int networkId = Integer.valueOf(cellularNetwork.toString()); // get
network id
// pass id to rtmp stream module
// and using fwmarkd bind socket to network

Native RTMP module:

const struct sockaddr_un SERVER_PATH = {AF_UNIX,
"/dev/socket/fwmarkd"};
int attach_socket_to_network(unsigned networkId, int socketFd) {
if (socketFd < 0) {
return -EBADF;
}
struct FCommand command = {SELECT_NETWORK, netId, 0};
int resp = send_command(&command, socketFd);
return resp;
}

Усю рутинну роботу з повертання зображення, співвідношення сторін, переініціалізації прев’ю з камери було зроблено самостійно вручну. Нині це все, мабуть, не актуально, тому що Google зовсім нещодавно (у вересні 2019 р.) в альфа-версії випустив для цього нормальну бібліотеку.

Основні фічі бібліотеки:

  1. Швидка робота стримінгу. Ядро написане чистою мовою C.
  2. Стабільна робота на всіх пристроях, підтримка застарілих і повільних пристроїв з Android 4.1+.
  3. Підтримка транслювання з: головної камери, фронтальної, екшен-камери (типу GoPro), екрана пристрою (скринстримінг).
  4. Автовизначення потужності пристрою і встановлення оптимальної конфігурації стримінгу.
  5. Стабільність роботи в мережах з поганим зв’язком, логіка реконектів.
  6. Підтримка перевертання пристрою, зміна розміру без переініціалізації стриму.
  7. Налаштування: розмір фрейма, якість, бітрейт, вимкнення аудіо тощо.
  8. Інтеграція за один клік. Підтримка Activities and Fragments.
  9. Типи масштабів, корегування співвідношення сторін, повний життєвий цикл оброблення стриму.
  10. Простий інтерфейс без зайвих залежностей.
  11. Підтримка armv7a, armv8-64 20. Фокусування на екрані тапом.
  12. Запис стриму у файл.

Для демонстрації роботи бібліотеки AVLib було створено невеликий застосунок, щоб перевірити попит на такі рішення. Лише декілька екранів, простий, але яскравий дизайн. Застосунок дає змогу за один клік миттєво створити трансляцію на свій канал у ютубі й поділитися прямим посиланням на стрим.

Дата публікації в магазині Google Play — лютий 2018 р.




Розвиток застосунку

Перші півроку все було передбачувано. Непоганий рейтинг, стабільний невеликий приріст нових користувачів, але досить слабкий ретеншен (загалом, люди недовго користувалися застосунком). Яскравий дизайн робив свою справу. Застосунок установлювали, але активно не використовували. Попри це застосунок дедалі вище тримався в списку ранжування магазину Google Play.

Побачивши, що залучення нових користувачів відбувається природним шляхом, ми сконцентрували зусилля на втриманні користувачів, дотримуючись такого процесу:

  • Генерування гіпотези на базі аналітики, результатів попередніх експериментів і зворотного зв’язку від реальних користувачів.
  • Реалізація і перевірка гіпотези.
  • Аналіз результатів.

Почали з додавання експериментальних фіч, які дають змогу втримати користувача в нашому застосунку (наприклад, чат).

Стратегія була така: формуємо беклог з фічами, одна фіча, реалізація, аналіз. Наші кроки:

  • Спочатку додали туторіал, який пояснював, що таке стрим, куди він стримиться, і що таке YouTube. Пояснення, які дозволи користувач дає нашому застосункові й для чого.
  • Декілька разів змінювали UI/UX, змінюючи іконки, кнопки й міняючи їх місцями. Чимало користувачів не розуміли, як створити стрим з екрана телефона або як змінити камеру для стрима Front/Rear. Спочатку в нас була одна кнопка перемикання всього, яку можна було клікнути, свайпнути або довго тримати, щоб перемкнути ― це було не очевидно. Не допоміг навіть екран з підказками, який ми додавали. У кінці вставили три кнопки, кожна з яких активує інше джерело стриму.
  • Додавання чату було однією з тих змін, які дали хоч і невеликий, але приріст часу користування.
  • Також спробували додати фічу запису стриму у файл, але користувалися нею зовсім мало.

Через декілька таких ітерацій, ретеншен і кількість активних користувачів збільшилися.

Потім ми зробили редизайн. Зберігаючи мінімалістичний функціонал, зосередившись на нюансах взаємодії користувачів з нашим застосунком.

Додали онбординг і розділ допомоги для користувачів, а також декілька дрібніших фіч. Після цього кількість завантажень та активних користувачів зросла. Усе було стабільно.




Проблема

Ми почали роботу над перетворенням нашої демки на повноцінний продукт, призначений для обізнаних геймерів-стримерів.

Після публікації чергового оновлення в Google Play з багфіксами, ми дістали відмову. У процесі листування зі службою підтримки в одній із чергових відписок побачили натяк на іконку нашого застосунку (яку, до речі, не змінювали від першого релізу). В одному з листів було зазначено: «Your app currently contains assets related to the app YouTube for Android TV». Ми нічого такого не використовували, але найбільш схожою на матеріали з YouTube з усіх наших небагатьох матеріалів оформлення була іконка.

Violation of Impersonation policy

Поточна іконка:

Змінили іконку ― знову реджект.

Знову листування зі службою підтримки ― причина така сама. Змінили іконку, деякі ресурси в застосунку ― реджект. Знову листування зі службою підтримки ― причина знову та сама. Нова іконка, оновлення дизайну ― апдейт прийнято.

У такий спосіб приблизно три тижні ґуґл не приймав наші оновлення. Зрештою через незрозумілі причини, приблизно за три реджекти поспіль, кількість інсталяцій, без будь-яких підстав почала пропорційно падати в усіх країнах у рази (на графіку падіння з початку квітня).

Останнє оновлення вже не врятувало ситуацію. Причина так і залишилася для нас незрозумілою. Схоже, що в алгоритмах Google Play реджекти, кількість порушень впливають на ранжування застосунків у результатах пошуку. А можливо, це звичайний збіг і проблема зовсім в іншому.

Це був крах. Апка різко опустилася в результатах пошуку, ніякі наступні оновлення не мали результату.




Втрата доступу

У застосунку YouStream використовується YouTube API для створення й керування трансляцією на своєму каналі. Від початку ми створювали демку й правильно не зберігали всі доступи. Тому доступ до облікового запису, під яким було створено проект у Google Cloud Platform для YouStream, було втрачено назавжди.

Розробляючи Android-застосунки, ми зазвичай для нових потреб створюємо новий тестовий акаунт у ґуґлі, тому що занадто велика інтеграція із сервісами, якими користуєшся особисто, і це не зручно. А також, коли ще треба комусь передавати керування, то логін з паролем від особистої пошти віддавати не хочеться. Наш застосунок використовує YouTube API v3, а це означає, що потрібно створювати проект у GCP й прописати в ньому авторизаційні дані від клієнтського застосунку (ім’я пакета й хеш ключа, яким підписано застосунок). Коли ми ще не планували такого розвитку, використали один з тестових акаунтів. Коли за три роки побачили, що є зростання і варто подивитися на статистику, чи не досягли ми лімітів ― перевірили всі тестові акаунти, до яких мали доступ, але ніде так і не знайшли дані про YouStream.

Щоб з тобою спілкувалися живі люди в підтримці ґуґл, потрібно бути платним користувачем GCP. Ми активували бронзовий пакет і на два тижні стали VIP-користувачами, яким відповідає підтримка. Після дуже тривалого листування з Google Cloud нам так і не вдалося відновити доступ до проекту в клауді для застосунку YouStream. Ми мали на руках лише ім’я пакета, хеш ключа і всі права на сам застосунок, але цього виявилося недостатньо для служби підтримки. Листування тривало близько двох тижнів. Приблизно після десятого листа служба підтримки зрозуміла, чого ми від неї хочемо й відповіла, що це неможливо, і що вони фізично не можуть відшукати в себе в клауді хеш нашого застосунку. Хоча, я думаю, що все вони можуть, але їм заборонено про це розповідати.

Висновки

Сам проект AVLib живе й надалі, але повернути популярність цьому застосункові нам не вдалося. Ознайомитися з ним можна в Google Play, а з бібліотекою, яка була використана в ньому, — на GitHub.

З набутого досвіду ми робимо для себе такі висновки:

  • Користувачам подобаються яскраві кольори. На старті будь-якого застосунку треба робити максимально красивий та яскравий дизайн, що привертає увагу. Красивий застосунок приємно мати у своєму телефоні. Також потрібна базова функційність, яку можна нарощувати в процесі. На прикладі YouStream після кожного етапу редизайну в нас зросли показники Visitors ― First-time installers.
  • За всяку ціну й максимально швидко потрібно виправляти помилки й оновлювати застосунок у Google Play після реджектів і банів.
  • Суворо дотримуйтеся правил у Google Play Market. Працюйте лише з досвідченими провайдерами або попросіть зробити аналіз когось із більшим досвідом публікування програм. Реджект застосунку може вбити органічний трафік і загрожувати вашій популярності.
  • Максимально ізолюйте ваші експерименти від зовнішніх факторiв (країна, сезонність, час доби і т. д.). Одна і та ж фіча може бути по-різному прийнята в різних країнах, в різний час. Тому необхідно враховувати це в експерименті і під час перевірки чергової гіпотези. Наприклад, викатуючи чергову фічу в період свят у вашiй таргет-країні ви, найімовірніше, отримаєте дуже спотворений результат, який може повести вас в iнший бiк від обраної ранiше стратегії.
  • Для того щоб ваш застосунок був популярний, він не обов’язково повинен бути складним. Радше навпаки: що простіший поріг входу користувачів, то ймовірніше, що користувач проведе в ньому більше часу. Безумовно, для цього потрібно, щоб ваш застосунок робив щось корисне.
  • Треба берегти ваш ґуґл-акаунт, використовувати 2FA.
  • Підтримка ґуґл, на жаль, відповідає дуже неохоче й без чітких рекомендацій, тому важливо покладатися на досвід і професіоналізм виконавців. В Інтернеті можна знайти чимало інформації про те, як підтримка реагує на стандартні запити й може через незрозумілі причини заборонити публікацію застосунку або примушувати розробників оновлювати старі застосунки, погрожуючи заблокувати весь ґуґл-акаунт. Тому тут ми не одинокі.

Основні виклики у роботі продакт-менеджера і як їх подолати. Частина 2

$
0
0

Я описав типові «менеджерські» виклики продакта у першій частиністатті. Ми говорили про активності, пов’язані зі стейкхолдерами, командою, керуванням очікуваннями й лідерством. У цій статті поговоримо про суто «продуктні» виклики, з якими стикаються продакти, і про інструменти розв’язання цих завдань.

Нагадаю, що я опитав понад 20 продактів, які працюють в Україні й за кордоном (Zalando, Booking, Vimeo, Grammarly, Badoo, ZeoAlliance, Tickets.ua, Edunav, Matic). Запитавши колег, що є для них найбільшими продуктними викликами, я отримав близько 20 варіантів відповідей, кожна з яких була практично унікальна. І це не дивно, бо в кожного продакта — різний бекграунд, досвід і досягнення.

Еволюція продукту й продуктних викликів

Завдання, що є найскладнішим для продакта у певний момент часу, залежить від етапу розвитку продукту, структури організації, сфери бізнесу, ситуації на ринку й стратегічних цілей компанії. Це щоразу потребує від продакта іншого набору знань і вмінь.

Наприклад, на етапі pre-market fit продакт-менеджер максимально зосереджений на з’ясуванні проблем користувача й валідації продукту на ринку. Критичними є ideation & customer development скіли, тоді як налаштування процесів, аналітика й стратегія ще на другому плані.

Під час швидкого росту продукту (scale-up) головне завдання продакт-менеджера — визначення й оптимізація метрик, гасіння пожеж з багами й перетворення MVP, зробленого абияк і скріпленого синьою ізострічкою, на стабільний продукт. Тому, як на мене, найскладніше на цьому етапі — а) правильно пріоритизувати завдання й б) побудувати таку систему роботи з метриками, яка дасть змогу бізнесу й продакту скласти з розрізнених даних цілісне бачення стану продукту.

MVP — головне, що працює!

У продукті на стадії maturity продакт зазвичай збільшує дохід або поліпшує досвід користувача в наявному продукті, розв’язує проблему масштабування, може відповідати за зовнішні партнерства, переймається стратегією продукту, балансує гнучкість з функціональністю системи... і ще 100 500 різних завдань, залежно від його позиції, досвіду, специфіки продукту.

Недарма, залежно від етапу розвитку продукту, Airbnb залучає продакт-менеджерів з принципово різним скілсетом, умовно об’єднуючи їх у три типи:

Докладніше про значення продакта на різних етапах розвитку проекту писали Андрій Кароль і Мері Ротар у цій статті.

Коли я почав працювати в Matic, компанія була на стадії pre-market fit, продажів було мало, моя CRM мала чотири користувачі, а сам продукт був проблемний. А за кілька місяців почався scale-up — етап бурхливого росту. Компанія організаційно виросла в чотири рази, а продажі за місяць — у 15 разів. Тому мені довелося зіткнутися з викликами, характерними для обох стадій розвитку продукту. І тому хочу поділитися з вами тими продуктними проблемами, які мені вже вдалося вирішити або над якими сушу голову тепер.

Максимізація імпакту

У статтях і книжках пишуть, що продакт-менеджер повинен постійно вдосконалювати власний продукт. І Scrum вчить нас, що потрібно постійно додавати «інкремент продукту». Але інкрементальні, поступові поліпшення з часом обов’язково морально зістарюються. Особливо це актуально для технологічних індустрій, схильних до радикальних змін, disruption. Наприклад, технологія дистрибуції відео інкрементально вдосконалювалася — відеокасети, CD, Blue-ray. І була повністю зруйнована Netflix і їх технологією потокової передачі. Прикладів десятки — кнопкові телефони Nokia, Kodak, що винайшов і заморозив виробництво цифрових камер, Microsoft, що «проспав» еру мобільних ОС...

Ларрі Пейдж сказав, що це природно для людей вибирати собі ті завдання, в яких точно не помилишся. Тому великий виклик для продакта — відмовитися від постійних невеликих удосконалень на користь того, що є потенційно disruptive.

Кен Нортон у продовження теми наводить приклад. Припустимо, у компанії є два потенційні продукти з однаковими витратами на розробку. Один з продуктів принесе $990 000 з імовірністю 99%, інший — $1 000 000 000 з ймовірністю 1%.

Угадайте, якому варіанту віддадуть перевагу? Точно, завжди оберуть перший, незважаючи на те, що математично варіант № 2 прибутковіший у десять разів!

Продакт повинен максимізувати імпакт з наявними в нього ресурсами.

Тому один з головних челенджів продакта — пошук ініціатив, які дадуть 10х, а не 10%, або ж експоненційне мислення. Наприклад, конкуруючи в ніші традиційного insurance comparison shopping, Matic залишався б компанією на 20 осіб з невеликими продажами ще, напевно, кілька років. Але завдяки одній з ініціатив, яка радикально змінила бізнес-модель, ми виросли за рік у п’ятнадцять разів, і продовжуємо шукати ідеї, які даватимуть експоненційний ріст. У цій статтіможна докладніше прочитати про те, як продакту розвивати експоненційне мислення.

Стратегія і тактика

Проте продакт не може сушити голову лише над тим, як стати новим Amazon чи Airbnb. І безлімітного рахунку в банку для шалених експериментів теж зазвичай немає. Бізнес росте, продукт еволюціонує, користувачі просять новий функціонал, конкуренти дихають у спину, ви боретеся з багами, знаходите десятки можливостей заробити більше або поліпшити досвід користувача тут і тепер.

Тому для максимізації імпакту продакту потрібно балансувати стратегію і тактику. Як поєднати стратегічні ініціативи із задоволенням негайних потреб клієнтів?

Пам’ятаєте правильну відповідь з першої частини статті? Правильно, it depends. Один з кварталів минулого року наша команда працювала над пожежами: багами й низькою продуктивністю системи, розбиралися з технічним боргом та іншими несексуальними речами. Наступний квартал провели, готуючи інфраструктуру продукту для майбутніх удосконалень. І тільки потім змогли взятися за реалізацію нових стратегічних проектів.

Тепер, плануючи роадмапу на квартал, приблизно 70% часу й ресурсів я закладаю на стратегічні ініціативи, які фокусуються на оптимізації core metrics-бізнесу, а 30% — на поліпшення досвіду користувачів і реалізацію їхніх запитів.

Але такий розподіл дуже умовний і непостійний, і залежить від етапу розвитку вашого продукту, його специфіки, а найголовніше — ключових цілей компанії в певний момент часу.

Постійно перевіряйте себе — чи робите ви тільки інкрементальні тактичні вдосконалення, які дадуть + кілька % а, може, ви півроку пилятимете лише один величезний проект, не приносячи в цей час ніякої доданої цінності?

Ставте собі запитання, де буде за рік-два не лише ваш продукт, а й бізнес компанії загалом? Як ваші стратегічні ініціативи допоможуть виконати стратегію компанії?

Пріоритизація

Окей, ми склали довжелезний список дуже важливих ініціатив, які враховують інтереси бізнесу, стейкхолдерів, користувачів, команди й допоможуть досягти цілей компанії. І наступний виклик для продакта — пріоритизація, або що, чому й коли ми повинні робити.

Про пріоритизацію в Інтернеті писано-переписано, є навіть агрегатор продуктних фреймворківз розділом про пріоритизацію, є й чудові статті на DOU Руслана Доронічеваі Сергія Бережного. Тому не заглиблюватимуся в теорію.

Кажуть, крутим продактом є не той, хто вирішує, що ми робитимемо, а той, хто швидко визначить, що точно НЕ варто будувати.

Тому, перш ніж братися за докладну пріоритизацію, я намагатимуся відповісти на такі запитання:

  • Чому це є проблемою?
  • Який розмір проблеми кількісно (втрати грошей, часу, недоотриманий прибуток, потенційний або наявний блокер)?
  • Чи можна розв’язати проблему без автоматизації? Ви здивуєтеся, скільки проектів дешевше робити в ручному режимі, ніж автоматизувати :)
  • Сustomer needs versus wants.

Головне у виборі техніки пріоритизації — оцінити масштаб проблеми, її терміновість і бенефіти від розв’язання. До того ж важливо уникати analysis paralysis — неможливості ухвалити рішення через брак даних. Для розв’язання цієї проблеми найліпше для мене працюють Cost/Benefit analysis і RICE/ICE — з огляду на відносну простоту й швидкість реалізації.

Додаткові чинники, на які я звертаю увагу, пріоритизуючи завдання:

  • Скільки користі ми принесемо (added value). Це може бути дохід, зменшення витрат, задоволеність клієнтів, enterprise valuе (знову ж, залежно від цілей компанії), — і чи є це найбільшою цінністю, яку можна додати (порівнюючи з іншими варіантами — opportunity costs).
  • Скільки коштуватиме наше рішення, порівнюю buy vs build.
  • Скільки коштуватиме і як складно буде підтримувати наше рішення з визначеними SLA, як легко воно масштабується.

Фокусування

Продакт завжди має десятки ідей, над якими варто подумати, сотні клієнтів, з якими треба поговорити, численні конкуренти, яких можна досліджувати, і безлімітний список статей у закладках, які хочеться прочитати. Якщо хапатися за кожну цікаву ідею або можливість, то дуже швидко ви станете вельми зайнятим менеджером, який веде команду й продукт на шаленій швидкості... у нікуди. Дуже легко стати зайнятим і важко залишатися продуктивним. Мені подобається, що про це говорить Сем Альтман. Він радить періодично робити паузи, щоб відповісти собі на запитання, чи рухається він у правильному напрямку.

То як не втрачати фокус?

По-перше, вам потрібна візія — всеохопна, довгострокова місія продукту. Vision statement розкриває прагнення продукту й лаконічно пояснює, яким повинен бути продукт, які конкретно зміни він утілить навколо себе.

По-друге, сильна стратегія, яка описує, як продукт допоможе досягти цілі компанії протягом свого життєвого циклу.

По-третє, потрібно сформулювати ініціативи, тобто відповісти для себе на запитання, як ви плануєте зреалізувати стратегію.

Для того щоб узгодити високорівневі стратегічні цілі з тактичним плануванням, ми в Matic використовуємо Objectives and Key Results framework. Про нюанси роботи з ним я писав у першій частиністатті.

Коли ви маєте візію і стратегію, що фокусує команду на проблемі, яку розв’язуєте, то точно знаєте, які запитання ставити користувачам, які дані шукати і які ініціативи найважливіші для реалізації стратегії. Здатність робити значні кроки в одному напрямку — це те, що визначає продакта Senior-рівня.

Доменна експертиза

По-справжньому розуміти проблеми користувачів продакт-менеджер може, отримавши хорошу експертизу в певній сфері бізнесу. Але не кожна компанія готова інвестувати в те, що продакт-generalist півроку розбиратиметься в новій для себе індустрії. Це стає ще більшим челенджем для продакта, коли працюєш на іноземному ринку, з його культурою, екосистемою, legal & compliance. Що ж робити?

По-перше, це чудова нагода ввійти в продакт-менеджмент, розвиваючись вертикально. Часто хорошими продактами стають після роботи customer success або support, маркетологи або девелопери, які добре розуміють бізнес-проблеми.

По-друге, можна вибрати домен, суміжний для різних індустрій. Cloud, BI або Payments Product Manager може робити продукти насправді для будь-якої бізнес-вертикалі. Паша Кузнєцов у подкастірадить фокусуватися на ніші, яка вирізнятиме вас серед інших. Наприклад, Martech або Automotive Product Manager отримуватиме значно цікавіші пропозиції роботи із хорошим бонусом за доменну експертизу.

Якщо ж ви продакт без глибокої спеціалізації, то доведеться максимально швидко вникнути в специфіку ринку. Для мене спрацювало таке.

По-перше, максимальне занурення «в поля». Перші тижні роботи я щодня проводив 3–4 години,спілкуючись з користувачами, стежачи за їхніми екранами, документуючи процеси. А за першої ж нагоди ми з командою поїхали on-site. П’ять днів разом з нашою sales-командою (що є кастомером мого продукту) дали в рази більше, ніж кілька тижнів вивчення документації.

По-друге, аналогії. Узагальнюйте, знаходьте спільне в тому, що вже знаєте. Наприклад, я швидко зрозумів, що процес продажу страхових продуктів, хоч і має особливості, проте є класичним продажем — з виявленням потреб клієнта і «закриттям» продажу, а поняття cross-sell, що знайоме мені з ритейлу, актуальне й для страхування.

По-третє, не бійтеся ставити дурні запитання. Пам’ятайте, product manager is the dumbest person in the room, але нічого страшного в цьому немає. Звичка розбиратися досконально в усьому новому — must have для продакта.

Дохідність

У коментарях до попередньої статті порушили тему впливу продакта на прибуток компанії: чи не є це найважливішим завданням і найбільшим викликом для продакта? І так, і ні.

Продакт-менеджер зазвичай безпосередньо не впливає на витрати компанії. Так, ми, продакти, розраховуємо витрати на розробку, підтримку, інтеграцію зі сторонніми сервісами, частково маркетинг, і на підставі цих даних ухвалюємо рішення buy vs build. Але продакти не ухвалюють одноосібних рішень про хайринг, компенсації і маркетингові бюджети, вибір інфраструктури або стратегічне партнерство. Тому продакт не є одноосібно відповідальним за дохід продукту або компанії.

Продакт-менеджер може мати значний вплив на Revenue. Як саме?

I have no f!@#$ing idea! Figure out how to make money with your product — that’s your job! © Dave McClure, Startup Metrics for Pirates.

Безпосередній вплив продакта на Revenue продукту актуальний для В2С історії, адже саме продакт має важелі керування ціною, CAC, ARPU, LTV, Retention й іншими страшними абревіатурами. Але для багатьох інших випадків вплив продакта на дохідність дуже опосередкований. Наприклад, як ви оціните revenue generation від продакт-менеджера функції Search у Google Maps? Під час роботи над внутрішніми продуктами (Internal Product Manager), найбільший імпакт й ефективність продакта — це те, як внутрішній продукт збільшує ефективність персоналу, і, у такий спосіб, зменшує витрати.

Продакт у традиційному В2В зазвичай суттєво не впливає на ціноутворення, а прайсинг продукту може бути кастомізовано залежно від клієнта й розміру угоди. Тому вплив продакта на дохід обмежується тим, як швидко і в якому обсязі будуть зроблені кастомні вдосконалення продукту, і чи знаходить продакт feature-market fit, який дасть змогу залучати нових клієнтів або зробити upsell наявним.

Резюмуючи

Що вирізняє крутого продакта — такого, який добре справляється з продуктними викликами:

  • шукає ініціативи 10х, а не +10%;
  • максимізує імпакт за допомогою наявних ресурсів;
  • балансує стратегічні проекти з досягненням результату тут і тепер;
  • фокусується на тому, що допоможе компанії досягти цілей вищого рівня;
  • уміє вибрати, коли потрібне «міцне» рішення, а коли згодяться «костилі»;
  • не тільки менеджить продукт, а й драйвить бізнесові ініціативи;
  • має глибоку доменну або технологічну експертизу;
  • ваш варіант (я впевнений, що кожен продакт може продовжити цей список).

Дякую, що дочитали. Поговорімо в коментарях або стукайте до мене в LinkedIn.

Куди рухатись далі, якщо ти — ВА

$
0
0

Я Тарас Мішак, Senior Business Analyst в EPAM Systems, Certified Salesforce Consultant, Certified SAFe Product Owner / Product Manager, у минулому фінансовий аналітик, Product Manager і Data Analyst. Ця стаття покаже вам кілька варіантів для зростання, якщо ви — Junior або Middle Business Analyst.

Отже, ваша початкова мета досягнута. Попередня позиція розробника, Quality Assurance Engineer, фінансового аналітика, банківського фахівця, наукового працівника / кандидата наук, викладача англійської, координатора проекту, офіс-менеджера або координатора з подорожей далеко позаду. Ви успішно пройшли і технічну, і проектну співбесіди, і випробувальний період, і перший-другий рік наполегливої роботи. І, перепочивши, ви задумуєтеся, що робити далі.

Почнімо з того, що, можливо, вам «нормально» й тепер. Ви успішно виконуєте свою роль ВА. Проект стабільний і навіть, раптом що, вас охоче візьмуть і в інший проект або компанію. Грошей вистачає і на вечірні посиденьки в барі, і на поїздку до Копенгагена на вихідні.

Я не сумніваюся, що:

  • ви постійно вчитеся, читаєте правильні статті на DOU та спеціалізованих ресурсах, відвідуєте всі семінари, ВА-мітапи й тусівки у вашому місті, а також конференції в інших містах і країнах;
  • вам вдалося змусити себе прочитати весь ВАВОК, а також почали готуватися до сертифікації CBAP з групою однодумців;
  • у вас була доповідь про requirements elicitation в місцевому ВА-осередку в компанії, а можливо, також і для ширшого кола;
  • на роботі у вас є ментор, що допомагає вам зростати надалі. Можливо, ви навіть маєте свого mentee ― QA чи навіть когось не з ІТ, якому ви допомагаєте освоїти ази ВА;
  • можливо, ви знайшли якісь додаткові курси, які проходите ночами онлайн, а також підтягуєте додаткові навички, наприклад знання бізнес-англійської, психології, public speaking або вміння їсти бамбуковими паличками.

Проте сумніви в тому, чи досить ви робите для того, щоб досягти успіху, у вас залишаються. Я був на вашому місці. І розумію вашу непозбувну бентегу. На різних етапах свого кар’єрного шляху періодично усвідомлював, що для прогресу мені треба рухатися далі — до нових челенджів. Наприклад, пропрацювавши в ІТ на різних посадах три роки, я приблизно той же період працював як фінансовий аналітик, щоб знову повернутися в ІТ уже в іншій ролі. Моїми критеріями не завжди були рівень зарплати й можливість її зростання, а й можливості розвитку, яку надавала та чи інша нова посада/компанія.

Шляхи розвитку

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

Але якщо гроші не єдина мотивація або якщо верхня межа можливої зарплати Middle ВА для вас не досить амбіційна, тоді варто подумати над можливими шляхами розвитку:

BI / Data Analyst / Data Scientist

Бувають ВА, які або через свій попередній робочий досвід, або освіту, або інтереси набувають навичок роботи з даними, базами даних, програмами з дата-аналізу й візуалізації даних, data science, machine learning тощо. Деякі з цих ВА хочуть розвиватися в цьому напрямі, залишаючись на тій же посаді. Але може бути й так, що ВА повністю перекваліфіковується у ВІ-аналітика або Data Scientist’а.

Тут немає готових рецептів для зміни, а буде чимало роботи, проходження онлайн-курсів, підтягування основ, зокрема з математики. Можливо, для швидшого занурення в нову професію вам доведеться здобути додаткову освіту в місцевому університеті або онлайн. Немає особливих заперечень, що це реально, але все ж варто розуміти, що, найімовірніше, науковці з інших галузей дадуть фору за багатьма параметрами абсолютному новачку в Data Science.

Senior Business Analyst

На мою думку, шлях до цієї посади може бути лише еволюційний. Бувають випадки, коли вже досвідчений працівник з іншої сфери вважає, що може «світчнутися» відразу на посаду Senior ВА. Основна проблема в тому, що, попри великий досвід, кандидат, найімовірніше, матиме прогалини навіть у базових поняттях, і від цього може не врятувати навіть виконання ролі ВА як додаткової у своєму проекті.

Тому найреальніший перехід з посади Middle ВА на Senior ВА через:

  • зміну компанії;
  • внутрішнього assessment’а.

Перший варіант можливий тоді, якщо у вашій попередній компанії взагалі не було ніякої градації, а ваш досвід на посаді ВА досить великий, щоб претендувати на цей рівень.

Другий варіант (assessment) — це перевірка ваших знань комітетом для визначення, чи ви вже досягнули вищого рівня кваліфікації. Їй передує досягнення певних критеріїв (роки роботи, поточний рівень, внесок у проект / BA Community тощо) і підготовка, що може передбачити проходження менторства в досвідченішого ВА. До перевірки залучають фахівців з інших локацій та, можливо, навіть зовнішніх експертів. Assessment відбувається у визначений час у забуканому мітинг-румі або на дзвінку. Після успішного проходження, вам присвоюють вищий рівень кваліфікації або надають рекомендації щодо поліпшення, щоб ви змогли краще підготуватися наступного разу.

Якщо анлокнути рівень Senior ВА, відкриються великі можливості, зокрема фінансові й нематеріальні заохочення. Визнається ваш авторитет, зростає рівень довіри до ваших рекомендацій, а також з’являються нові можливості розвитку, наприклад участь у більших проектах і релокація.

Business Analysis Team Lead

Наступний ранг в ієрархії ВА — Team Lead, що передбачає наявність не лише більшого досвіду, а також команди ВА під вашим мудрим керівництвом. Тут виникає потреба в додаткових скілах, а також умінні відпускати надмірний контроль (мікроменеджмент) і делегувати роботу. Підготувати до цієї трансформації може роль ментора для ВА й координація роботи інших членів команди в поточному проекті.

Посада ВА Team Lead дасть змогу звантажити рутинну роботу на підопічних і зосередитися на вищому «стратегічному» рівні. З іншого боку, як і на всіх посадах лідів, виникне ризик утратити деякі базові навички.

У деяких компаніях розвиток на цьому не зупиняється, і є також ще вищий щабель. Зрештою, треба розуміти, що досконалість насправді не має меж.

Resource Manager

Інший спосіб набути досвіду керівництва — посада ресурсного менеджера. Resource Manager — це фахівець тієї ж спеціалізації, що й ви (у вашому випадку ВА), який курує вашу діяльність, допомагає з онбордінгом й адаптацією, скеровує вас надалі в розвитку, дає вам відгук про вашу роботу на підставі зібраної інформації і позитивно або ні стимулює за виконану роботу. Ставши RM, ви зможете по-новому подивитися на роботу підопічного ВА та його шлях розвитку. Ви зможете бути ментором або навіть коучем, а можливо, і розширити свій досвід завдяки обговоренню якихось нетипових ситуацій у проекті.

Недолік цієї позиції, яка є додатковою до вашої основної, — те, що насправді у вас тепер буде дві роботи, що потребують одночасно уваги. З іншого боку, можливо, вам це сподобається і ви захочете розвивати себе в цій додатковій ієрархії.

Project Manager

Працюючи пліч-о-пліч з проектним менеджером, ВА набуває чимало додаткових навичок і досвіду, що дає змогу порівняно просто перейти в PM track. Найліпшим рішенням у такій ситуації було б менторство PM, проходження додаткових курсів з Project management, а також виконання функції координатора у своєму проекті. Також у вас з’явиться нова улюблена книжка на чимало сторінок (PMBOK), а також може замайоріти перспектива сертифікації із цієї системи або, наприклад, PRINCE2.

Ставши PM, ви можете й надалі лишитися ВА, виконуючи в проекті одну з цих ролей або навіть паралельно дві. Але також ви можете повністю зосередитися на проектному менеджменті й розвиватися у межах цієї ієрархії. Серед переваг ― вам набагато легше буде зрозуміти й працювати з ВА у вашому проекті. Серед іншого ― вам треба буде прийняти новий рівень відповідальності й сфокусувати увагу на інших нюансах виконання проекту.

Product Owner / Proxy Product Owner / Product Manager

Порівняно новим напрямом розвитку для ВА є посада Product Manager або Proxy Product Owner. Ще досі точаться дискусії (holywar), наскільки реально можна стати повноцінним Product Manager в аутсорсі. Є варіант PdM в аутстафі, що, можливо, буде наближеніше. Або можна бути PdM аутсорсингової компанії для її внутрішніх та не лише продуктів. Також точно можна сказати, що вже тепер немало ВА виконують функцію Proxy PO, тісно взаємодіючи зі своїм РО й командою.

Мабуть, найліпший варіант рухатися в бік повноцінного PdM — релокація й робота онсайт у представництві тієї ж компанії-виконавця або самого замовника. Додатковою допомогою в цьому напрямі може стати підготовка до сертифікації, зокрема ISPMA, SAFe Product Owner / Product Manager тощо. Безцінною є участь у мітапах з чинними PdM’s, які охоче діляться своїми факапами. За ідеального розвитку подій варто бути готовим до нового рівня відповідальності, а також більшого навантаження і потреби далі чимало вчитися і прогресувати.

Business/ Technology/ Experience Consultant

Нарешті, один з нових трендів — трансформація з бізнес-аналітика в консультанта. Можна змінювати посаду поступово через надання консалтингових послуг у своєму проекті, залучення ВА до регулярних пресейлів, а також освоєння ним/нею певного домена або платформи на високому рівні. Найбажаніший кандидат на таку посаду — консультант з інших галузей з тривалою роботою в певному бізнес-домені або ВА з великим досвідом у пресейлах і воркшопах.

Як консультант, можна зосередитися на таких напрямах:

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

Technology Consulting потребує серйозного розуміння технологій і платформ, щоб ви могли допомогти знайти або запропонувати рішення, що відповідатиме не лише функційним, а й нефункційним вимогам клієнта, наприклад розгортання на більшій кількості підрозділів і додавання нових модулів. Хороший хід для цього — вивчення певної платформи й лінійки продуктів від SAP, Salesforce, Adobe тощо. З переваг ― це дасть вам чітке розуміння можливостей продукту OOTB і можливого мапання функціоналу з потребами замовника.

Якщо ви давно цікавитеся напрямом дизайн/UI/UX і до того ж маєте досвід роботи з підготовкою wireframes/mockups/prototypes, могли б експлуатувати свої знання і здібності, як Experience Consultant. Додаткова передумова для цього напряму — досвід роботи дизайнером.

Обирайте свій шлях

Зазначений перелік варіантів не претендує на вичерпність або глибину розкриття. Кожна з опцій потребує виваженого рішення й серйозної підготовки, а також відкриває нові можливості розвиватися надалі. Важливо, щоб вам було цікаво рухатися в обраному напрямку, бо ризик вигорання в деяких з цих варіантів не виняток. Один з підходів у виборі кар’єри — порівняння ваших поточних навичок зі сподіванями щодо нової посади. Найліпшими помічниками в цьому стануть ваш Resource Manager і колеги, які вже пройшли цей шлях. Я бажаю вам успіхів у цій нелегкій справі й вірю, що вам вдасться реалізувати задумане.

Кросс-культурная коммуникация на английском в IТ: нюансы, которые важно знать

$
0
0

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

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

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

Вежливость

Если вы общаетесь на английском, вы предполагаете, что люди будут себя вести и общаться так, как в англоязычных странах. Это было бы идеально. Тогда все были бы приветливыми и вежливыми. Но, конечно, культурные особенности дают о себе знать, и наш человек все равно, говоря по-английски, скажет: Do this for me! — приказным тоном вместо: Could you do this for me?или Would you mind doing this for me? (я бы выбрала второй вариант с would).

Знание языка на высоком уровне позволяет избежать недопонимания в момент общения. Вы знаете, что на стендапе, задавая вопрос: Why don’t you give me your updates?— ваш менеджер на самом деле вежливо просит вас рассказать о проделанной работе, а не упрекает: «Почему ты мне не рассказываешь о проделанной работе?!» Why don’t you — вежливый оборот для выражения просьб.

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

Для нас нормально говорить и писать кратко, по делу, без прелюдий. Для носителей языка — наоборот, и они не считают, что это вежливо: они просто так говорят. Особенно остро тема politenessчувствуется, когда нужно выразить просьбу. Мы думаем, что сказать: Send me this document, —может быть не очень вежливо, но если добавить please, это прозвучит вежливо и по-деловому. Но нет. Такая фраза создает у носителей языка впечатление, что вы недовольны чем-то, разозлились или даже кричите. Это звучит как приказ, требование (demand), а не просьба (request).

Как лучше формулировать просьбы? Как минимум начинать с Can you ...?, а лучше — Could you ...?Примеры вежливых фраз для различных ситуаций можно взять из таблицы ниже. Рекомендую распечатать и повесить возле рабочего места — будет классной шпаргалкой, когда пишете имейл или говорите на митинге.

Types of requestsSituationsLanguageNotes
Simple requests (the reader has an obligation to comply with your request)Asking an employee or coworker to do something that he/she normally handlesCould/can you ...please?
Would you mind ...?
Would you mind is a bit more polite than can/could you...? Also, could is a little more polite than can. Remember that mind is followed by the gerund (-ing). For example, Would you mind taking to take a look at this.
Favors and big requests (the reader does not have an obligation to comply with your request) Getting help with something that you normally handle yourself
«Special» requests
I was hoping you could...
I was wondering if you could...
Do you think you might be able to...?
Favors and big requests require «softer» language. One way to make language softer is by using past tenses (I was hoping, I was wondering, I wanted to, etc). Another way we make language softer is by using modals (might, could, etc).
PermissionVacation requests
Time off requests
Borrowing something
I was hoping I could...
I was wondering if I could...
Would it be okay if I...?
Do you think I might be able to...?
Just like with favors and big requests, we use softer language to ask for permission (past tenses, modals, etc).
Suggestions Offering your point of view
Suggesting a better way of doing something
What about if...?
Maybe we could...?
I thought it might be a good idea to...
Using these expressions sounds more polite than Well, I think we need to...
Rejecting/Refusing/DecliningRejecting offers, suggestions,
proposals/ideas
I’m not sure that...
I don’t know if...
Using tentative language like I’m not sure that... and I don’t know if, sounds more polite than that won’t work.
Pointing out a mistakeBilling mistakeIt looks like...
It seems...
These introductory statements help to make the message softer.
Checking on the status of somethingFinding out if something is finished
Gently reminding someone that you need something they are working on
Have you had a chance to finish/work on...?Have you had a chance to finish/work on... is much more polite than Are you finished yet? or Is it ready yet?

Обратная связь

Тема обратной связи очень неоднозначна в разных культурах. Здесь хотела бы привести пример из книги The Culture Map by Erin Meyer, где автор делит страны на высококонтекстные и низкоконтекстные. То есть те, где люди напрямую говорят друг другу, что думают, и наоборот — ходят вокруг да около, используют много метафор в разговоре.

The Culture Map, by Erin Meyer

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

Но так дело обстоит не во всем. Американцы ведут себя как японцы, когда речь заходит об отрицательном фидбэке. То есть если вашему клиенту/менеджеру/коллеге что-нибудь не нравится или он не хочет этого делать, напрямую он вам этого либо не скажет, либо скажет так, что вы не поймете, что вас только что поругали. Он-то думает, что вы понимаете, что это плохо, но для вас это звучит как «может быть» или даже «все хорошо»:

  • I don’t think it’s the best solution (означает плохое решение, мы не будем его использовать).
  • There is something about this button style. It’s hard to see it’s a button (переделайте кнопку).
  • It’s ok (это не очень).
  • It’s a very interesting way to put it (мне не нравится, как вы это сделали).

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

Что касается американцев, могу сделать вывод из огромного опыта общения и сотрудничества: они всегда любезны, много хвалят, не скрывают своих чувств, что для нас порой выглядит странно. Например, ваш коллега или менеджер может воскликнуть: I love you! - радуясь подписанному контракту. Но американцам очень сложно критиковать. Вернее, они-то критикуют, но мы не всегда это улавливаем.

Например, индусы и китайцы всегда соглашаются на любую просьбу, а потом ничего не делают. Опять же, это часть их культуры, они не могут сказать «нет». Вам придется много раз уточнять и проверять, чтобы понять, делают они что-то или нет.

Страны Северной Европы, наверное, самые прямолинейные с критикой. Говорят в лоб — не обижайтесь.

У украинцев обычно нет проблем с тем, чтобы покритиковать кого-то, но есть проблемы с похвалой. То есть американцам нужно учиться у нас критике, а нам у них — похвале.

Ниже несколько советов, как лучше коммуницировать по-английски на работе.

Общаясь на английском, старайтесь быть как можно менее прямолинейны, не критикуйте человека в открытую. Вместо Do (something) pleaseлучше сказать: Could you do (something) please?или Would you mind doing (something)?Вместо I don’t like what you didлучше сказать: It doesn’t look right to me или I don’t feel right about that. Главное — не «тычьте пальцем» и не спешите использовать слово you. Лучше перенесите акцент на себя или используйте безличные конструкции вроде It seems...There seems to be... и т. д.

Старайтесь как можно чаще делать чекины с вашим собеседником и позволять ему выразить свое мнение, согласие или несогласие. Для этой цели можете использовать следующие распространенные фразы: How do you feel about...? Does it make sense? Will it work for you? Would you be ok with that?

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

  • I’m afraid — боюсь, что...
    Спасительная фраза для того, чтобы отклонить любую просьбу: I’m afraid, I can’t help you. I’m afraid, it’s not gonna be possible.
  • I have noticed that — я заметил, что...
    Для тех случаев, когда вы чем-то недовольны или вас что-то смущает: I have noticed that you haven’t replied to my calendar invite. Will we see you in tomorrow’s meeting?
  • I’m not sure about that — я не уверен насчет этого.
    На самом деле значит «я не согласен» или «мне это не нравится», а если вы в самом деле не уверены, лучше сказать: I don’t know about that.
  • Not really — не совсем.
    Очень простая фраза, которую часто используют носители языка, потому что просто no зачастую звучит слишком грубо: Would you like to work on this project? — Not really, I was hoping for something a bit more challenging.
  • I am going to need you to... — мне нужно будет, чтобы ты/вы...
    Если вы даете задачу кому-то, а не просто просите что-то сделать, эта фраза как раз для этого. Не забудьте про going to, так будет намного вежливее, чем если вы скажете: I will need you to. Например: We’re expecting a surge in user traffic in the next month so I’m gonna need you to make sure that we can handle it.
  • Will do — сделаю, будет сделано.
    Обратная фраза, в случаях, когда вам дают какую-то задачу. Звучит более приветливо, чем просто ok. Could you forward that email to me? — Ok, will do.

Small talk — обязательный навык для коммуникации

Small talk — это больная тема для украинцев. Мы не умеем заполнять эти неловкие паузы, когда ждем начала митинга или пришли сделать кофе на кухню, а там коллеги стоят и надо вроде что-то сказать, а не знаешь, что и как начать.

Но! Small talk — это большая и очень важная часть коммуникации на английском, как в повседневной жизни, так и в деловой среде. Будьте готовы, что ваши коллеги или незнакомцы, если вы за границей, заведут с вами разговор. Не стесняйтесь и сами заговорить первым. Ниже несколько советов, о чем поговорить и как начать small talk:

Говорите о погоде, новостях, трафике, отпуске, еде или смотрите по ситуации.Иногда можно просто прокомментировать то, что сейчас находится перед глазами, и так завязать разговор.

Как начать говорить:

  • Используйте tag-question. Так часто называют «вопросы с хвостиком», когда вопрос состоит из утвердительной части и «хвостика» в конце. Например: It’s so warm outside, isn’t it? You work for Google, don’t you? Tag-questions — отличный способ завязать разговор, при этом вы не будете выглядеть слишком напористо, как если бы задавали прямой вопрос: Do you work for Google?
  • Начните с вопроса с Present Perfect: Have you seen the latest Joker movie? или Have you been to this Indian restaurant that just opened?
  • Сделайте комплимент: I like your cardigan. Looks perfect for this chilly weather.

Всё это называется soft skills, навыки общения. И если вы уже специалист с хорошими hard skills, добавить вам soft skills — и будете на вес золота для любой компании. Кстати, все мои знакомые из Google единодушно заявили, что им пришлось в какой-то момент сделать основной упор именно на развитии своих soft skills (выступали на митапах и конференциях, писали статьи для блога компании и прочее), чтобы их заметили. Именно это помогло им устроиться на работу в эту компанию.

Не избегайте смолл-тока. На конференциях и митапах вам от него никуда не деться — это часть вежливой культуры общения. Не обязательно говорить о погоде или уличных пробках, необходимо лишь проявить открытость и готовность к общению. Задайте несколько простых вопросов, чтобы узнать больше о собеседнике, например: What brings you here? Is it your first time here? What does your company do? What do you do at company X? Are you enjoying the food/music etc?

Во время деловых переговоров смолл-ток не должен затягиваться надолго, но хотя бы в первую минуту общения он должен присутствовать. Обычно простого How are you doing?или How is your day going / Are you having a nice day?вполне достаточно. Когда захотите перейти к делу, делайте это мягко. Вместо Let’s startлучше сказать I guess we can get started или Let’s / I guess we can jump into it.

Вот еще несколько полезных фраз, советов для small talk:

  • How is your day going so far?— Как ваш день?
    Хороший способ «прощупать» настроение собеседника и завязать разговор. Это не How are you?, которое, по сути, является не вопросом, а, скорее, приветствием. На этот вопрос принято отвечать более развернуто, чтобы показать, что вы открыты к общению.
  • I love your outfit. Where did you get it?— Мне нравится, как вы одеты. Где вы это купили?
    Комплименты — хороший способ завязать беседу. Возможно, начав говорить про одежду, вы через пять минут обнаружите, что обсуждаете блокчейн.
  • Are you more of a cat person or a dog person?— Тебе/вам больше нравятся коты или собаки?
    Конечно же, вы не зададите такой вопрос в первые секунды знакомства. Однако удивительно, насколько носители языка любят вопросы такого плана и используют их для поддержания и оживления беседы. Не обязательно спрашивать о животных, можно также спросить: Are you more of a tea person or a coffee person? Are you more of an introvert or an extrovert?и т. д.
  • So, you like «Breaking Bad», huh?— Вам нравится сериал «Во все тяжкие», правда ведь?
    Когда разговор застопорился, можно вернуться к старой теме, и это huh?, по сути, значит «расскажи мне больше».
  • Tell me more about it — Расскажите об этом.
    Спасительная фраза для смолл-тока, ведь иногда проще слушать, чем говорить. I tried taking painting classes once. — Really? Tell me more about it.

Emailing — пишем часто, но не всегда правильно

Emailing — это отдельная и по-своему болезненная тема. Однако здесь в каком-то плане даже легче, так как правила письменного общения более строгие и четкие по сравнению с устным, и разница культур не ощущается столь ярко.

В этикете IТ-emailing (здесь я выделяю именно IT-emailing, так как стиль будет отличаться от типичного делового письма, например, в банковской сфере) преобладает стиль semi-formal, он характеризуется отсутствием излишне навороченных «канцелярских» фраз, присущих стилю formal, и сленговости, присущей стилю informal. Также важно помнить следующее:

  1. Имейл должен быть четко структурирован и разделен на параграфы.
  2. Каждый параграф должен содержать одну идею.
  3. В конце обязательно должен быть call to action или побуждение к какому-нибудь действию.

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

  • Please note that...— обратите внимание, отметьте, что...
    Please note that next coming Monday is a day off so I will be out of office but still available by phone.
  • With regards to/Regarding...— по поводу... что касается...
    With regards to your question about payment methods, here is a list of possible options.
  • As discussed / As agreed — как мы договаривались...
    As agreed, I am sending you the notes for the presentation. Let me know your thoughts.
  • Feel free to...(reach out / ask more questions...) — не стесняйтесь, делайте что-то без колебаний.
    Feel free to contact me if you have any questions or concerns.
  • Please let me know if...(you have any questions or concerns, you’re gonna be able to do it) — пожалуйста, сообщите/дайте мне знать.
    Please let me know if the estimations you gave us last time are final and can be used going forward.

Также многие забывают об этике ответа на имейлы. В англоговорящем мире деловой переписки принято отвечать на имейл в течение 24 часов. Если необходимо показать свою ответственность и важность письма, то отвечают в течение 2-3 часов.Оставить письмо без ответа считается в бизнесе дурным тоном. К сожалению, многие постоянно так делают, даже не подозревая, что это неправильно. Например, если вам пришло письмо, и у вас нет информации, о которой вас просят, не нужно ждать, пока она появится, чтобы ответить. Лучше сразу отписать и сказать как есть. Как вариант, можно ответить вот так:

Thanks for reaching out. At this moment I don’t have the information you are asking me about. But I will definitely get back to you as soon as I have it.

Полезные ресурсы

Если мы хотим избежать недопонимания, работая в мультинациональных командах и проектах, рекомендуют сразу обсудить кросс-культурный аспект, чтобы все члены команды понимали, почему могут возникнуть проблемы и как быть. Если вы менеджер, можете провести собрание, где расскажете про разницу культур по книге The Culture Mapи порекомендуете ее прочесть команде.

Kim Scott, известный коуч из Кремниевой Долины, член университета Apple и бывшая сотрудница Google, в своей книге Radical Candorнастоятельно советует американцам начать давать и неприятную обратную связь, а не говорить всегда: Oh, you are doing great. Если вы видите, что другой человек делает что-то неправильно или плохо, ваша обязанность — ему об этом сказать. Он может даже не знать, что делает что-то не так, а потом может оказаться поздно. Очень рекомендую прочесть Radical Candor, особенно тем, кто руководит командами. Книга хорошо прокачивает менеджерские скиллы, плюс, если прочтете на английском, почерпнете много новых классных идиом.

Если вы часто ездите в бизнес-командировки или к вам приезжают гости, вам еще пригодится знание этикета поведения в офисе и за его пределами. Пожимают ли в офисе руки, когда здороваются? В Штатах — не всегда, и точно не стоит обходить всю комнату и жать руку каждому. Стоить ли подавать женщине руку при знакомстве? В бизнес-мире женщина всегда протянет руку поздороваться при первом знакомстве, поэтому не робейте и отвечайте взаимностью.

Как себя вести на деловом обеде в ресторане? А если у вас «микс» гостей из разных стран? Здесь все индивидуально, но могу посоветовать отличную книгу, в которой вы найдете подробную информацию о правилах поведения и общения в бизнес-среде в любой стране мира. Называется Kiss, Bow, and Shake Hands by Terri Morrison. Рекомендую сделать ее настольной, если работаете с представителями многих стран. Ну, или по крайней мере изучить главы о тех странах, с которыми работаете.

Кроме книг, для понимания американской культуры общения и поведения полезен просмотр тематических ТВ-шоу и сериалов. Например, можно посмотреть одноименный сериал о Кремниевой Долине (Silicon Valley) — он даст представление, как работают стартапы. Но не принимайте все за чистую монету, в сериале много сарказма. Зато английский очень живой, узнаете множество новых слов и идиом, которые действительно используются в общении и письменной коммуникации в IT.

Еще рекомендую, особенно если вы стартап и готовитесь питчить инвесторам, посмотреть ТВ-шоу Shark Tank. Кроме того, что научитесь правильно питчить на английском, так еще и получите классные примеры, как нужно давать отрицательный фидбэк и говорить «нет», чтобы никого не обидеть и не перейти границу дозволенного в американской культуре общения.


Читайте также:

OS Daemonology: виды, преимущества, подводные камни

$
0
0

Демоны, агенты, хелперы — да кто они все такие?!

Меня зовут Владимир. Я занимаюсь macOS-разработкой уже около 6 лет. За это время работал «от и до» — от дизайна окошек и кастомных кнопок до системного программирования, секьюрити и написания Kernel-модулей.

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

Статья будет полезна любым технически направленным специалистам; всем, кто хочет понимать, как работают приложения и сама ОС (причем не только macOS, но и прочие *OS) «под капотом»; и конечно, тем, кто хочет знать, как и когда использовать тех или иных демонов при разработке своих приложений.

Что мы видим и в чем правда?

Как обычный пользователь видит операционную систему со своей стороны?

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

При этом очевидно, что для работы как этих приложений, так и всей системы должно быть что-то еще.

Демоны внутри!

Демоны?

В разговорной речи обычно можно услышать термин «демон», что хоть и неправильно, но используется повсеместно. Стоит помнить, что Daemon != Demon. Согласно исторической справке, daemon — это некое создание, порожденное богами для выполнение различного рода работ, которыми сами боги заниматься не хотят. Эта терминология впервые была использована в так называемом демоне Максвелла.

Так кто же такие все эти daemons и зачем они нужны?

Это в первую очередь OS support, а также мониторинг, функции серверной части приложений, task managers. Они выполняются в background и (обычно) лишены какого-либо GUI.

Сам daemon в системе операционных систем *OS представлен конфигом (.plist) с описанием демона плюс непосредственно исполняемым файлом.

Конфигурационный файл .plist описывает и задает поведение демона.

Например:

  • Label — идентификатор демона:
    com.example.mydaemond
  • ProgramArguments — аргументы командной строки, используемые для старта демона:
    /bin/rm, -rf, “/tmp/trashfiles”
    /Library/Application Support/CoolApp/com.coolapp.monitord

Еще есть большое количество прочих аргументов, таких как:

  • KeepAlive (launchd перезапустит процесс, если процесс будет завершен);
  • RunAtLoad (launchd запустит процесс на старте (системы/логин-сессии));
  • WorkingDirectory (рабочая директория процесса);
  • WatchPaths (пути файловой системы, за которыми будет следить демон).

Note: вы можете подробнее прочесть об этих и прочих атрибутах файла конфигурации в мануале к launchd:man launchd.plist.

Также можно обратиться к официальной статье Apple.

launchd — самый первый демон

В *OS-системах есть «особый» демон, имя которому — launchd.

launchd — это аналог init UNIX-подобных систем. launchd всегда имеет pid 1 (pid 0 — это сам Kernel).

Основные задачи launchd:

  • инициализация системы;
  • запуск/перезапуск процессов;
  • поддержка демонов;
  • поддержка XPC.

Занятный факт: у launchd есть персональный Twitter.

*OS Daemonology в деталях

Note: это описание приводится на примере macOS как системы с открытым доступом к файловой системе. При этом устройство прочих ОС семейства Apple (iOS, watchOS...) аналогичное.

Apple предлагает классифицировать всех демонов по следующим категориям:

XPC Service

Пожалуй, XPC Service — это самый пропиаренный и задокументированный вид демонов в macOS.

Apple сами рекомендуют использовать XPC Service в приложениях. Их даже можно встраивать во фреймворки.

Основные факты о XPC Service:

  • App Store friendly;
  • специальная цель в Xcode;
  • работает как текущий пользователь;
  • GUI-less;
  • stateless (может быть уничтожен при запуске на IDLE);
  • относится к конкретному приложению;
  • живет не дольше, чем родительское приложение;
  • может быть установлен в изолированном приложении.

Резюмируя, можно сказать, что XPC Service позволяет вынести часть кода логики в отдельный процесс, при этом предоставляя удобный механизм взаимодействия между процессами «из коробки».

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

Важные заметки о XPC Service:

  • не может быть пользователем root;
  • не может использовать какие-либо функции графического интерфейса (Windows, уведомления, ...);
  • может быть non-sandboxed, даже если основное приложение (конечно, не в AppStore).

UserAgent

Довольно популярной разновидностью демонов является UserAgent. Он представляет собой самостоятельный background-процесс, запущенный от текущего пользователя. Обычно существует 1 процесс UserAgent для каждого конкретного пользователя системы (для каждой логин-сессии).

Основные факты о UserAgent:

  • независимое приложение;
  • работает как текущий пользователь;
  • может иметь состояние;
  • может отображать графический интерфейс (окна, уведомления и т. д.).

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

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

Note: установить UserAgent в систему можно только из НЕ-Sandbox-приложения.

Vanilla daemon

Daemon как daemon (в классическом понимании) — это системно-глобальный процесс, запущенный от root. Daemon существует один для всей системы, а также может стартовать в момент загрузки ОС и до процедуры аутентификации пользователя.

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

  • выполнение привилегированных операций (от root);
  • хранение глобального состояния независимо от логин-сессий;
  • координация множества UserAgent;
  • установка KEXT.

Основные факты о Daemon:

  • независимое приложение;
  • запускается как root;
  • может иметь состояние;
  • может запускаться при загрузке OS;
  • singleton;
  • GUI-less.

Note: Apple НЕ рекомендует использовать демонов, предлагая заменить их UserAgent или в крайнем случае PrivilegedHelper из соображений безопасности. Если следовать этой рекомендации, то риск компрометации пользовательских данных и системы в целом снижается.

PrivilegedHelper

PrivilegedHelper можно назвать «урезанным» демоном.

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

По своим свойствам это такой же Daemon, но при этом существует ряд особенностей:

  • должен быть установлен исключительно с помощью API SMJobBless;
  • при установке копируется в /Library/PrivilegedHelperTools.

Казалось бы, что такого в копировании в /Library/PrivilegedHelperTools?

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

Для решения вопроса можно идти различными путями:

  1. Статические библиотеки.
  2. Runtime-линковка.

Основные факты о PrivilegedHelper:

Note: PrivilegedHelper НЕ может быть установлен из Sandboxed-приложения.

LoginItem

LoginItem является совместимым с App Store (и урезанным) вариантом UserAgent. По сути, это обычный UserAgent, который устанавливается с помощью легального API, предоставляемого Apple.

Основные факты о LoginItem:

  • пользовательский агент с «ограниченным» доступом;;
  • устанавливается через SMLoginItemSetEnabled;
  • может быть установлен в изолированном приложении;
  • автоматически запускается при входе пользователя в систему;
  • руководство по использованию.

Note: это НЕ тот LoginItem, который можно увидеть в Settings.

Где обитают демоны?

Как говорилось ранее, демон — это пара из файла конфигурации и самой исполняемой программы. Где разместить сам исполняемый файл, не имеет ровным счетом никакого значения, так как это просто путь в секции Program/ProgramArguments.

Файлы конфигурации размещаются: ПутьПример

Daemons:

/Library/LaunchDaemons

/Library/LaunchDaemons

/System/Library/LaunchDaemons

Global (all-users) Agents:

/Library/LaunchAgents

/Library/LaunchAgents

/System/Library/LaunchAgents

User-specific Agents:

~/Library/LaunchAgents

/Users/JohnDoe/LaunchAgents

/Users/JaneDoe/LaunchAgents

...

~/LaunchAgents

Отличие глобальных UserAgent от user-specific в том, что глобальные агенты будут ассоциированы со всеми пользователями ОС. При этом user-specific-агенты ассоциированы только с конкретным пользователем.

Note: чтобы поместить агента в «глобальные», требуются права root.

Преимущества демонов

Мы рассмотрели основные актуальные на сегодняшний день виды демонов в семействе систем *OS. Часто задаваемый вопрос — «Зачем мне XPC Service, если я могу просто запустить еще один поток?».

Существуют следующие преимущества использования демонов в различных их проявлениях.

EXC_BAD_ACCESS, crash-resistance

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

В случае с «классическим» монолитным подходом к приложению пользователь теряет все и сразу. Однако если вынести части кода по отдельным сервисам, то самое худшее, что нас ждет, — это потеря только одной фичи (при этом с показом нефатальной ошибки). Конечно, такой подход несколько усложняет архитектуру системы. Но что такое небольшая сложность перед такими огромными преимуществами, как crash safety?

Security

И нет, не сесюрити. Размещение своего кода в демонах сильно усложняет задачу злоумышленникам и просто реверс-инженерам, которым по какой-то причине захотелось разобраться в том, как же работает ваш код. Код, который находится во фреймворке, крайне легко эксплуатировать и изучать путем динамического анализа (простыми словами, дебагом). Код, который «вшит» в демона, уже так легко не подебажишь, а также не используешь напрямую. Как минимум пока защита системы не упала, а также пока у anonymous нет прав root.

Ограниченное использование root

Вынося код, требующий root, в отдельный демон, идем по принципу «не используй лишнее без необходимости». Этот принцип пронизывает также идеологию, продвигаемую Apple, которая говорит: «Используй модули Kernel только в том случае, когда не можешь обойтись привилегированным процессом. Используй привилегированные процессы только для тех операций, которые действительно этого требуют». Выделяя самый минимум функционала в рутовые демоны, мы обеспечиваем защиту не только своих приложений, но и всей системы и данных пользователя на тот случай, если в нашем демоне найдется хотя бы маленькая дырочка.

Личные демоны

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

При выборе между XPC Service, UserAgent и LoginItem предлагаю руководствоваться следующими соображениями и практическим опытом.

XPC ServiceLoginItemUserAgent

  • AppStore-friendly;
  • всегда упакованный;
  • может быть встроен во фреймворки;
  • нет графического интерфейса;
  • stateless.

  • AppStore-friendly;
  • может иметь графический интерфейс;
  • всегда упакованный;
  • нативная установка API;
  • SMLoginItemSetEnabled;
  • сложности при обновлении / переустановке;
  • неочевидные способы коммуникаций;
  • недокументированное поведение при запуске / повторном запуске.

  • может иметь графический интерфейс;
  • пакет или исполняемый файл;
  • гибкий launchd.plist;
  • процедура установки с помощью инструмента «launchctl»;
  • для установки требуется non-sandboxed app.

Контроль демонов — launchctl

Если XPC Services, LoginItem, PrivilegedHelper будут (должны) работать «из коробки», то, чтобы завести полноценного UserAgent или Daemon, придется уже работать с системными тулзами.

macOS предоставляет тулзу launchctl, посредством которой можно:

  • получить список всех «демонических» проявлений, зарегистрированных для каждого пользователя;
  • загрузить/выгрузить демона;
  • включить/выключить демона и т. д.

Examples

List agents for current user: launchctl list
Load agent: launchctl bootstrap gui/501 /Library/LaunchAgents/com.company.launchagent.plist
Unload agent: launchctl bootout gui/501 /Library/LaunchAgents/com.company.launchagent.plist
where <user’s UID> is user id (uid_t)

List daemons: sudo launchctl list
Load daemon: sudo launchctl bootstrap system /Library/LaunchDaemons/com.company.launchdaemon.plist
Unload daemon: sudo launchctl bootout system /Library/LaunchDaemons/com.company.launchdaemon.plist

Более полная документация по launchctl находится по ссылкам: Launchctl 2.0 syntaxи cheatsheet.md.

Это только начало

Изначально я планировал разбить эту тему на две статьи: одну — о разновидностях демонов в *OS плюс описание XPC как механизма межпроцессного взаимодействия, вторую — о low-level-реализации XPC. Однако, как обычно, материала вышло больше, чем предполагалось. Потому об XPC речь будет идти в отдельной статье.

Подписывайтесь и следите за интересными статьями и code sample: GitHubи Twitter.

Секретные техники проработки требований. Часть 2

$
0
0

Мы уже рассмотрели в первой частиследующие техники: stakeholder analysis, user story mapping, ролевая модель и сценарии использования, прототипирование. Во второй части я продолжу делиться с вами техниками, которые помогают мне проверять требования на полноту.

Объектно ориентированная модель

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

Случай из жизни

В одном проекте я работал с начинающим бизнес-аналитиком. Попросил в документ с требованиями добавить объектно ориентированную модель. Моя просьба поставила аналитика в тупик, у нас получился следующий диалог:


Аналитик:Это все очень сложно и непонятно.
Я:В каком смысле сложно и непонятно?
Аналитик:Я не знаю, с какой стороны подойти к этому вопросу.


Я:Ок, давай рассмотрим твою семью как некую целостную систему, в которой есть объекты — члены твоей семьи. Перечисли их, пожалуйста.
Аналитик:Папа, мама, я, брат и сестра.
Я: Теперь давай определим взаимосвязи. Ты с папой связан как сын, у тебя есть брат и сестра, они также связаны с твоим папой. Верно?
Аналитик:Да, верно.


Я:Ок, тогда мы можем назвать эту связь родительской. Связь твоего отца с детьми — один ко многим. Точно такая же связь у вас с мамой. Верно?
Аналитик:Да, все верно.
Я:Теперь давай рассмотрим связь отца с мамой. Они связаны как муж и жена. Вопрос: может ли у твоего отца жен быть больше, чем одна?
Аналитик:Нет.


Я:Какая в этом случае связь между родителями?
Аналитик:Один к одному.
Я:Верно. Какие еще могут быть связи?
Аналитик:К примеру, у меня нет детей, и в теории их может не быть — связь ноль ко многим.


Я:Верно. Пример, конечно, не очень, но надеюсь, что детки у тебя будут :)
Аналитик:И в то же время у меня может быть только один отец и одна мама.
Я:Эта связь — обязательно один экземпляр.
Аналитик:У меня еще кот есть, и он как бы тоже член семьи. Какая у него будет связь с нашей семьей?


Я:Давай порассуждаем. У тебя может быть больше одного кота?
Аналитик:Да, конечно.
Я:Соответственно, у кота также более одного хозяина — ты, брат, сестра, мама, папа?
Аналитик:Верно.


Я:Тогда связь — многие ко многим.
Аналитик:Ага, понял.
Я:Также у каждого объекта есть параметры. Вот какие параметры ты сможешь перечислить?
Аналитик:Имя, фамилия, отчество, возраст...
Я:Верно, все, что интересно о человеке в рамках определенной ситуации. Также следует учитывать и контекст возможных ситуаций. К примеру, если ваша семья переедет в восточные государства, где разрешено многоженство, твоему отцу можно будет иметь более одной жены, и связь уже будет один ко многим.

Далее мы определили объекты будущей системы и описали их. С помощью UML (Unified Modeling Language), используя диаграмму классов, мы визуализировали связи между объектами.

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

Название атрибутаТип атрибутаВалидацияКомментарий
1IDЧислоУникальный идентификатор. Генерируется системой. Начальное значение — 1. Каждое следующее значение на 1 больше предыдущего
2ФИОТекстЗаполняется пользователем в момент создания
3Дата рожденияДатаdd.mm.yyyyЗаполняется пользователем в момент создания
4Дата созданияДатаdd.mm.yyyyЗаполняется системой
5ФИО автораТекстЗаполняется системой. Источник данных — справочник «Пользователи системы» (см. раздел «Справочники»). Указывается ФИО пользователя, инициировавшего создание карточки

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

Такая техника разработчикам поможет спроектировать систему, а заказчику — понять, с какими объектами ему предстоит работать. К примеру, ему нужно будет наполнять справочники. Встает вопрос: как их поддерживать в актуальном состоянии, если в справочнике могут быть тысячи или десятки тысяч значений? Возможно, понадобится мастер-система для интеграции.

Диаграмма состояний

Моя дочь учится в 3-мклассе, в один из вечеров она подошла ко мне и спросила: «Папа, сегодня в школе нам рассказывали, в каких состояниях может быть вода. Зачем вообще мне нужно знать о состояниях воды? Как мне это поможет?»

Я:Чтобы понять, как использовать воду в зависимости от ее состояния. Вода может быть в трех состояниях: жидком, твердом и газообразном. Как мы можем использовать воду в жидком состоянии?
Дочь:Пить, плавать, варить в ней еду...


Я:Супер. А еще строят гидроэлектростанции, которые вырабатывают электричество, — вот, смотри на картинку в учебнике. Теперь давай разберемся, как мы можем использовать воду в твердом состоянии.
Дочь:Кататься на коньках, охладить колу...


Я: Верно, молодец! Лед очень важен в нашей жизни, его еще используют в медицине и в природе. В горах есть ледники, это природный накопитель пресной воды. Когда они тают, образуется река. А в газообразном какая вода? Можешь привести пример?
Дочь: Тучки, из которых идет дождь. Он поливает цветы, траву и деревья.


Я:А еще в промышленности. Там до сих пор используют паровые двигатели. Теперь ты понимаешь, зачем знать о состояниях воды?
Дочь: Да, спасибо.

Собственно, так же важно понимать, какие формы может принимать объект. Это очень полезно при описании требований к автоматизации бизнес-процессов. Рассмотрим пример автоматизации процесса согласования отпуска. У нашего объекта «Заявка на отпуск» будет такой жизненный цикл:

  • создание;
  • согласование;
  • выполнение;
  • архивирование.

На каждом этапе объект будет иметь свое состояние или статус.

Жизненный цикл и состояние объекта

Этап жизненного циклаСтатус
СозданиеЧерновик
СогласованиеСогласовано
СогласованиеОтклонено
ВыполнениеВ работе
АрхивированиеВыполнено

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

Возможные операции с объектом в разрезе статусов

Этап жизненного циклаСтатусРольОперации
СозданиеЧерновикАвторРедактирование
ЧерновикАвторУдаление
ЧерновикАдминистраторУдаление
СогласованиеСогласованиеРуководитель автораСогласование
СогласованиеАдминистраторУдаление
ОтклоненоРуководитель автораОтклонение
ВыполнениеВ работеБухгалтерРасчет отпускных
В работеБухгалтерНачисление отпускных
В работеАдминистраторУдаление
АрхивированиеВыполненоАдминистраторУдаление

Для визуализации состояний объекта я использую диаграмму состояний UML (Unified Modeling Language). Пример диаграммы состояния — на рисунке.

Каждое состояние объекта должно быть описано. При описании не стоит забывать, что существуют ограничения в каждом состоянии. К примеру, в состоянии «Черновик» объект не должен никто видеть, кроме «Автора» и «Администратора», после согласования никто не может редактировать атрибуты объекта и так далее.

Также каждое состояние объекта может спровоцировать определенное событие в системе. При выполнении операции «Отклонить»:

  • «Руководитель автора» должен указать причину отказа — добавляется диалоговое окно для комментария;
  • система должна инициировать отправку уведомления «Автору» об отклонении заявки на отпуск. Возникают вопросы: от чьего имени должно приходить уведомление «Автору», какова тема уведомления, какой текст уведомления?

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

Техника CRUD

Аксиома в бизнес-анализе: если в системе возможно создать объект, то его можно просмотреть, отредактировать и удалить.

В переводе с английского слово crudимеет несколько весьма забавных значений. В ИТ аббревиатура CRUD означает названия четырех базовых операций с объектом:

  • create — создание;
  • read — чтение;
  • update — обновление;
  • delete — удаление.

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

Давайте рассмотрим прототип создания объекта «Служебная записка».

Как видите, при создании объекта пользователю предлагают заполнить 8 атрибутов. После выполнения операции «Сохранить» система дополняет объект системной или вычисляемой информацией, и в режиме просмотра (чтения) пользователю будет виден объект с 12 атрибутами.

А в режиме редактирования пользователь может изменить только 6 атрибутов.

При редактировании объекта не стоит забывать, что также могут быть ограничения в зависимости от ролевой модели и состояния объекта. К примеру, только «Бизнес-администратор» может изменить поле «Важность», и только в случае, если карточка в состоянии «В работе». Поля «Инициатор» и «Подписант» может редактировать только «Секретарь».

Важно помнить, что для операции «Удалить» необходимо предусмотреть диалоговое окно с подтверждением намерения удаления объекта из системы.

В большинстве случаев заказчики хотят иметь некое хранилище для удаленных объектов — «Корзину». Также могут быть требования ко времени хранения объектов в «Корзине».

Навигация

— Скажите, пожалуйста, куда мне отсюда идти? — спросила Алиса.
— Это во многом зависит от того, куда ты хочешь попасть, — ответил Кот.
— Да мне почти все равно... — начала Алиса.
— Тогда все равно, куда идти, — сказал Кот.

Льюис Кэрролл. Алиса в Стране чудес

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

Для себя я выделяю следующие типы навигации:

  1. Ключевая — самая важная. Сюда я включаю главное меню системы или, если мы работаем над веб-приложением, хедер (Header). В хедер может входить главное меню сайта, языковая навигация, ссылка на стартовую страницу.
  2. Второстепенная — на втором месте после ключевой, однако не стоит забывать, что она также важна для пользователя.
  3. Рекламная — используется на внешних ресурсах.
  4. Тематическая — ссылки на близкие по тематике разделы. К примеру, тип новости (спорт, криминал), теги.
  5. Указательная — показывает пользователю, в какой части системы он находится.
  6. Контекстная — гиперссылки в отображаемых текстах.

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

  • Какая страница системы стартовая?
  • Как попасть на эту экранную форму или в этот режим работы?
  • Куда я могу перейти с этой экранной формы?
  • Как мне понять, где я сейчас нахожусь (какой шаг процесса, какой объект отображает экранная форма)?
  • Как вернуться назад?
  • Kак отменить предыдущее действие?

В итоге

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

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

До встречи на страницах ДОУ ☺


Как и для чего собеседовать работодателя

$
0
0

За 18 лет работы я провел больше 1000 собеседований на самые разные позиции. В 90% случаев у них была одна общая черта: кандидаты не интересовались тем, что их ждет, глубже, чем «какой проект, сколько денег, какая команда». И дело не в том, что им не было важно это знать, — дело в самой культуре собеседований.

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

Зачем читать эту статью

Каждый из вас как минимум один раз в жизни был на собеседовании. Большинство читали «полезные советы», как показать себя в выгодном свете, расположить к себе собеседника, понравиться и даже договориться о более выгодном предложении. По сути, основное внимание таких материалов сосредоточено на том, как попасть в компанию, как заинтересовать ее. Однобоко, не находите? Не хватает чего-то очень важного.

Википедия говорит: «Собеседование — встреча с потенциальным работодателем или его представителем при приеме на работу. Цель собеседования — познакомиться воочию, понять, насколько работодатель и соискатель подходят друг другу, а также обсудить детали сотрудничества».

«Подходят друг другу» — это взаимная осознанная заинтересованность. Именно этому посвящена статья: зачем собеседовать с работодателем, как это делать и на что обращать внимание.

Как мы можем понять, подходит ли нам новое место работы?

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

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

Точки интереса

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

Соглашение о сотрудничестве, равно как и любое другое, предполагает ответственность сторон. В контексте вопроса «что придется делать» аспект ответственности дает нам еще два фактора: за что и чем отвечает специалист, а вместе с ним — за что и чем отвечает работодатель.

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

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

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

Готовимся собеседовать

К любому важному делу стоит хорошо подготовиться. Определите темы, которые вы хотите прояснить для себя. Можно написать список конкретных вопросов, создать структуру mind map или fishbone, по которой и идти (я предпочитаю не записывать вопросы: так беседа проходит как беседа, а не как блиц «Что? Где? Когда?», и гораздо проще перестроиться в ходе общения). Второй шаг — настроиться на то, что вы имеете полное право задавать вопросы и получать информацию. Это нужно, чтобы не сбиваться в ходе общения и не смущаться от реакций собеседника. Третий шаг — заключительный. Еще раз подумать над тем, что действительно важно в новой компании, после чего переделать список вопросов.

Как задавать вопросы

Иногда лучше понять, что вы не пара, до начала отношений, а не потом, когда у вас квартира в ипотеке, общий PlayStation и собака. Да, иногда отношения не складываются. И если при знакомстве с потенциальным партнером по жизни неудобно спрашивать «что стало с тем, кто занимал это место до меня», «как вы относитесь к совмещению основной занятости с дополнительными работами» и «каким было самое неудачное сотрудничество», то в общении с работодателем это вполне уместно. Главное здесь — правильно задавать правильные вопросы.

Я как представитель компании на собеседованиях очень радуюсь вопросам и всегда охотно на них отвечаю. На любые. Даже настаиваю, чтобы ребята их задавали. Отвечаю на вопросы со всем старанием и максимально честно. Но даже такая активная позиция не всегда работает. Например:

ВопросОтветЧто понял кандидатЧто имелось в виду
Компания поддерживает развитие сотрудников?Поддерживает, у нас даже есть институт менторства.Вау! Целый институт, меня будут учить как в школе, читать лекции.Есть менторы, которые поддерживают и помогают в самостоятельномизучении.
У вас гибкий график?Да, можно договориться с проектом о рабочем графике.Я смогу как и раньше работать по 6 часов как мне удобно и репортить по 8!Можешь работать когда удобно, если все обещанное делаешь в срок.
У вас интересные проекты?Вполне.Мне интересен blockchain, будет именно такой проект.Мне интересны алгоритмы расчета эффективности солнечных панелей в консольном исполнении — проект интересный.
У вас дружный коллектив?У нас очень сплоченная команда.Ура! Меня примут в семью.Вся команда дружит против нового ПМа и на этой почве сплотилась. Вероятно будут дружить и против тебя, наш новый брат :)
Ребята горят своей работой?Еще как!Они энтузиасты, изобретают подходы и творят!Ребята горят изо всех мест и на проект, и на технологии, и на менеджмент, аж дым стоит. Бедные.
Как мне поднять уровень дохода со временем?Подтвердить соответствие следующей позиции.Я умный. Я быстро учусь. Закрою скиллы и буду счастлив.Требований к позиции 50 штук, одно из них — 3 года непрерывной работы в проекте. Удачи, брат :)
Как вы относитесь к овертаймам?Плохо.Вот он — менеджер моей мечты! Будет меня оберегать.Плохо и утомительно объяснять команде почему мы не заплатим им за овертаймы. Жаль, что их так много.

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

«О, ваша компания входит в топ-357!» Рейтинги, размер компании, фотография директора на фоне Ниагарского водопада, красивый стенд на выставке — все это говорит о том, что компания действительно обладает этими формальными признаками. Все. Больше ни о чем это не говорит. Ни о том, что следующим на Ниагару поедете вы, ни о том, что работа в этой компании даст +100 к уважению среди коллег, ни о чем таком.

Часто формальные признаки трактуются мозгом как начало цепочки if — then. Это хорошо и эффективно для мозга, который любит оптимизировать и упрощать даже в ущерб целостности информации. Если помнить, что вся цепочка — это предположения, а не факты, легко избежать необоснованных заключений.

Совет № 1.Задавайте вопросы, что это будет значить лично для вас, на что повлияет в работе.

Совет № 2.Если заметили, что у вас уже сформировались цепочки if — then, выпишите звенья предположений и уточните, как с ними обстоят дела на самом деле

«Ну это понятно». Словом «ключи» мы называем очень разные штуки — гаечные, скрипичные, цифровые, ключи от машины, наконец. Если собеседник сразу понял, о каком ключе вы говорите, так это только потому, что вы с ним находитесь в одном контексте. Общаясь с представителем компании, вы совершенно точно упускаете часть контекста. Ваш собеседник отвечает в своем контексте, вы слышите ответ в своем. Вопрос задан, ответ получен, но картинки в головах разные.

Совет № 3.Задавайте уточняющие вопросы: «как конкретно происходит этот процесс у вас», «что именно вы подразумеваете под этим термином», «какие люди обеспечивают выполнение этих задач». Чем лучше вы прочувствуете «текстуру» и детали ответов, тем больше шансов понять ответ именно так, как собеседник его понимает и как хотел донести.

«У вас же есть спортзал? Коллектив у вас дружный, правда?» Вот какие ответы вы думаете получить на эти вопросы? «Ну гантель была где-то, мы ее найдем. А ребята, прямо скажем, склочные, не дружат». Такие? Сомневаюсь, что даже очень открытый собеседник вам так ответит, даже если это правда. Мы хотим выглядеть хорошо в глазах других и — о новость! — иногда приукрашаем или невольно искажаем действительность. А тут и вопросы, прямо скажем, сами подталкивают. Зачем искушать собеседника? Наша задача — получить достоверную информацию. Получить, обдумать и принять взвешенное решение.

Совет № 4.Задавайте открытые вопросы: «расскажите мне, пожалуйста, об этом», «как у вас устроена вот эта часть», «опишите пожалуйста, как вы действуете в такой ситуации».

«Звучит логично». С помощью логики можно обосновать абсолютно все. Что крокодил больше зеленый, чем длинный, например. Практического смысла нет, но забавно. Или что тот же крокодил больше широкий, чем зеленый. Или что он больше крокодил, чем широкий. Уже тревожно, не правда ли? Мы вполне уверенно можем объяснить почти любое явление или сущность, используя логику — свою, а не логику создателя этой сущности. Мы можем выстроить предположения, трансформировать догадки в аргументы, но в итоге ожидания не выдержат столкновения с реальностью. Досадный ненужный опыт.

Совет № 5.Уточняйте. Не стесняйтесь спрашивать, почему так, как они к этому пришли. Это помогает хотя бы немного понять логику собеседника.

Какие вопросы мы задаем

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

Что придется делать.С первого взгляда тут все понятно — есть вакансия, там четко написано о работе с передовыми технологиями над креативными задачами. Так принято писать о вакансии, и работодатель тут ни в коем случае не обманывает. Часто все так и есть — часть работы действительно такая классная. Часть может быть скучной или даже неприятной — про такое все договорились в объявлениях не писать. Но ни то ни другое не дает понимания, чем будет наполнен ваш день, что составляет серединку этой «параболы крутости задач», каким будет типичный средний день — не хороший и не плохой. Лучше всего говорить с ребятами из похожего проекта или с теми, кто выполняет такие же функции в компании. Если средний день с его задачами, ритуалами и отчетами вам покажется нормальным, есть шанс, что все будет не так уж плохо ☺

За что и чем отвечает специалист.Зачем компания хочет нанять специалиста, что и кому пообещали в этой связи, чего от вас будут ждать, какие результаты вы должны показывать? Простые, но важные вопросы. Каждая заявленная вакансия — это попытка отразить в требованиях к кандидату то, что автору кажется необходимым для достижения уже сформированных к этому моменту целей и (плохо, если так) взятых компанией обязательств. Вам совершенно точно нужно это знать до начала сотрудничества. И не менее важно понять, что будет, если вы не справитесь. Есть ли у вас право на ошибку? На сколько ошибок и каких? Даже если вы любите риск, узнавать, что в компании есть денежные штрафы, лучше не в тот момент, когда их вам выписывают.

За что и чем отвечает работодатель.Работодатель должен платить зарплату. Это основное. Еще хорошо бы, чтобы он приносил плюшки, решал мелкие вопросы вроде налоговой и парковки, плюс другие мелочи. Есть и другие вопросы. Что будет, когда проект закончится? Работодатель готов гарантировать новый или будет месяц бенча и прощание? Он отвечает за то, чтобы у вас был стол, ноутбук и выход в сеть или еще немножко за то, чтобы вам нравилось работать? Перед командой он отвечает за то, чтобы она делала именно тот интересный модуль, ради которого ее собрали, вместо бесконечного багофикса, или нет? Очень глубокая и многогранная тема. Выбирайте, что именно вам важнее всего узнать, и спрашивайте.

Ах да, чем именно отвечает работодатель перед вами — это вопрос контракта. Внимательно читайте. Внимательно!

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

Критерии оценки успешности и эффективности.Что вообще компания продает? Резюме, лицензии, услуги, или, может, клиент отдает 10% прибыли своего бизнеса? Как конечные клиенты компании определяют, достигнуты ли поставленные цели, как измеряют эффективность работы? Идеально, когда эти принципы у компании работают и в сторону клиентов, и в сторону сотрудников. Если это не так или такая информация закрыта, уточняйте, что именно оценивают и почему так. Соблюдать правила и соответствовать ожиданиям гораздо проще, если четко их знаешь и понимаешь.

Механизмы оценки.Рано или поздно нашу работу оценивают. Очень хочется, чтобы ее оценили достойно и, наверное, даже чуть лучше, чем она есть на самом деле. И приятно, и мотивация появляется — о-го-го. Я ни в коем случае не призываю обманывать тесты, подкупать оценивающих или выдавать чужой код за свой: рано или поздно это все равно всплывет. Я призываю разобраться, как демонстрировать собственные достижения в работе и принимать решения о том, насколько это вам подходит.

Принцип определения added value.Узнайте, как в компании оценивают принесенную сотрудником пользу. Звучит похоже на предыдущий пункт, но это другое. Когда-то давно мне посчастливилось работать с парнем лет 16 всего. Пройти собеседование для него было непосильной задачей: он сбивался и терялся, сам факт оценивания был для него невыносимым стрессом. Рассказать, что он сделал, было проблемой: застенчивый был парень. Но при всем при этом он был одним из самых талантливых и продуктивных программистов в небольшой на то время компании. Приносил ли он пользу? Еще какую! Можно ли было выявить это с помощью базовых механизмов оценки — внутренним собеседованием и фидбэками от клиента? Нет. Он получил шанс поработать и показать результат. Его смогли оценить по этому результату, по added value, а не по формальным критериям. К чему эта история? Узнавайте, как оценивается приносимая вами польза самой компанией. Если вы узнали себя в этом герое, убедитесь, что в компании есть тот, кто увидит ваш потенциал, кто сможет работать нестандартно.

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

Принципы взаимодействия с коллегами.Дружный коллектив. Когда кандидаты озвучивают такое ожидание, я спрашиваю: «Что, если коллектив сдружился и сплотился вокруг идеи никого нового к себе в команду не принимать?»

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

Базовые ценности команды и компании.Тут просто. Попросите собеседника рассказать, какие ценности команды и компании он разделяет. Своими словами, со своим отношением к ним. Нам не нужны красивые миссии и цели с графиками. Советую не принимать заученных клише и штампов (leverage our synergies) в качестве ответов. Вряд ли живой человек в команде реально работает ради «эффективной капитализации активов корпорации на международных рынках». Для того чтобы составить мнение, достаточно честного ответа собеседника. Лучше, конечно, двух или даже трех ответов.

Как происходят изменения.Что, если вас устраивает почти все и вот это «почти» не дает принять решение? Что, если нравится абсолютно все в компании, но страшно: вдруг произойдут перемены и вы не сможете вернуть все как было? Что, если через год вы остро захотите заниматься другими задачами? Все это об изменениях.

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

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

Важны реальные примеры изменений, которые уже произошли. Если на вопрос, что вы можете изменить, на что можете влиять, ответ будет: «Да что угодно, на все можно влиять», но без реальных примеров, то вы не проверите это утверждение. А проверить нужно, ведь со временем мы захотим не просто пилить гаечку, мы захотим менять свое окружение, делая его лучше.

Вместо заключения

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

Chrome DevTools: налаштування, можливості та способи перевірки коду

$
0
0

Для чого потрібні Chrome DevTools?Буває так, що написаний код невідомо чому відмовляється працювати. Щоб швидко з’ясувати, у чому проблема з кодом й ефективно її розв’язати, потрібно опанувати певний набір інструментів. Chrome DevTools — один з найпотужніших інструментів веб-розробника. Його вбудовано в найпопулярніший нині браузер Chrome.

Кому це буде корисно?Усім, хто займається веб-розробкою і переймається своєю ефективністю. Однозначно must read для початківців. Я впевнений, що кожен читач зможе знайти в цій статті щось нове й корисне, незалежно від свого досвіду в розробці.

Чому я вирішив про це розповісти?Мій досвід як програміста починався ще до виникнення Chrome, і знайомство з DevTools відбувалося поступово від початку їхнього створення. Однак про багато можливостей цього інструменту я дізнався вже навчаючи програмувати в академії.

Коли викладаєш програмування для початківців, помічаєш, чого може не вистачати, щоб ефективно виконати завдання. Часто перешкоджає брак досвіду, а отже — відсутність у свідомості студента вже готових шаблонів, що розв’язують поставлене завдання. Та навіть тоді, коли студент здогадався, який підхід варто застосувати, його може спіткати несподіванка: написаний код працює зовсім не так, як повинен би!

Продемонструвати зацікавленому читачеві, як швидко розв’язувати подібні проблеми за допомогою Chrome DevTools, і стало мотивацією для написання статті.

Виклик і налаштування

DevTools містить кілька вкладок, кожну з яких ми проаналізуємо нижче. Викликати інструменти розробника можна кількома способами, і зручніший з них слід вибрати залежно від ситуації:

  • Щоб дослідити якийсь елемент на сторінці (у вкладці Elements), на нього потрібно клацнути правою кнопкою миші й вибрати пункт Inspect.
  • Щоб швидко відкрити вкладку Elementsбез конкретного виділеного елемента, можна скористатися комбінацією клавіш Ctrl + Shift + C або Command + Option + C (Mac).
  • Щоб швидко опинитися у вкладці Console, натисніть Ctrl + Shift + J або Command + Option + J (Mac). З допомогою цієї ж комбінації клавіш DevTools можна закрити.

Налаштувати інструменти можна, натиснувши три вертикально розміщені крапки в правому верхньому куті їхнього вікна (поруч із хрестиком). За допомогою меню, яке з’явиться, можна вибрати варіант розміщення DevTools: унизу сторінки, з якою ви працюєте, у лівій або правій її частині чи взагалі в окремому вікні. Імовірно, час від часу вам доведеться перемикатися між цими варіантами, адже, залежно від розміщення елементів на сторінці та їхнього розміру, мати панель з інструментами буде (не)зручно в різних місцях.


З того ж меню, вибравши Show console drawerабо More tools, відкривається доступ до додаткових можливостей інструментів розробника (див. останній розділ нижче). Пункт Shortcutsдасть змогу переглянути основні комбінації клавіш, які суттєво пришвидшують роботу з DevTools, а пункт Settings — налаштувати інструменти й сторінку загалом. Скажімо, пункт налаштувань, яким я користуюся найчастіше, — відключення скриптів на сторінці (Disable JavaScript). Він, щоправда, допомагає мені здебільшого не як розробникові, а як користувачеві інших сайтів: інколи стає в пригоді, коли треба відключити некоректну клієнтську валідацію полів або зупинити скрипт, що без причини вантажить процесор.

Вкладка Elements

Перша і для багатьох випадків основна вкладка інструментів — Elements. Тут розробник може переглянути дерево HTML-елементів і редагувати їхній код «на льоту».



Для того щоб перейти до потрібного елемента на сторінці, можна натиснути кнопку з курсором у лівому верхньому куті вікна DevTools, а потім — у тому місці на сторінці, де відображено елемент. Інший спосіб — відразу знайти потрібний елемент серед дерева, яке відображає DevTools. Для цього рухайтеся між рядками дерева, натискаючи кнопки вниз і вгору на клавіатурі, а коли певний рядок потрібно буде розгорнути й відобразити його дочірні елементи, натисніть клавішу зі стрілкою праворуч (звичайно, для такої навігації можна користуватися й мишею). У будь-який момент Chrome підсвічуватиме на сторінці виділений елемент синім кольором; крім того, за допомогою клавіші Hцей елемент можна тимчасово приховати, а потім відобразити знову, а за допомогою клавіші Deleteвідразу видалити.

Під час зневаджування (debugging) скриптів, що маніпулюють деревом елементів, незамінною може виявитися функція зупинки (breakpoint) на подію зміни елемента. Для її активації клацніть правою кнопкою на елементі у дереві, виберіть Break on, а далі один з трьох варіантів: зупинятися, коли скрипт вносить зміни у структуру вкладеного дерева цього елемента; коли міняється атрибут; або коли елемент збираються видалити.



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

Щоб змінити тег елемента, якийсь його атрибут або текстовий вміст, двічі клацніть цей тег, атрибут або текст, уведіть нове значення й натисніть Enter. Інколи хочеться змінити увесь код HTML-елемента. Тоді вам знадобиться клавіша F2 (або можна клацнути елемент правою кнопкою миші й вибрати Edit as HTML). У нижній частині вікна DevTools продублює «шлях» до поточного вибраного елемента.



Проте велику частину функціоналу містить і права частина вкладки Elements, що, своєю чергою, містить кілька панелей-підвкладок. На панелі Stylesможна переглянути всі присвоєні виділеному елементу стилі разом з назвою CSS-файлу і номером рядка, в якому задано відповідне правило. Стилі можна швидко відключати й підключати знову, натискаючи прапорець ліворуч від конкретної властивості. Ще можна змінити значення властивості, натиснувши її, а також додати нову CSS-властивість, натиснувши праворуч від якої-небудь з наявних.



Угорі списку CSS-правил міститься поле швидкого пошуку потрібної властивості. Туди можна ввести, наприклад, «color» або «black» і побачити, яке правило задає елементу чорний колір. Поряд з полем є кнопки, що дають змогу навісити на елемент нові класи або навіть псевдокласи. Останнє буває зручно, коли потрібно розібратися в стилях, що з’являються в елемента лише тоді, коли на нього наведено курсор або коли цей елемент стає активним.



Під списком CSS-правил (або за маленького розміру вікна — праворуч від них) з’являється діаграма блокової моделі виділеного елемента:



Елементи блокової моделі (рамка, відступи, позиція) виділяються однаковим кольором одночасно в цій діаграмі й на самій сторінці, тому їх легко можна зіставити. Окрім того, відображувані величини можна редагувати безпосередньо в самій діаграмі, двічі натиснувши відповідне значення.

У другій панелі — Computed — можна переглянути конкретні значення, які браузер присвоїв тим або іншим CSS-властивостям. Скажімо, замість формального color: inheritтут можна побачити фактичний color: rgb(0, 0, 0), а замість width: auto — width: 1234px. Також, якщо розкрити властивість, натиснувши стрілку поруч з нею, з’явиться ланцюжок правил, що її зачіпають; переписані (відхилені) значення закреслено:



Для елементів, що містять текст, унизу панелі відображено інформацію про фактичний шрифт, який використано для його промалювання. Цей шрифт може відрізнятися від прописаного у font-family, якщо, наприклад, потрібного шрифта у розпорядженні браузера не виявилося.



У наступній підвкладці — Event Listeners — браузер відображає всі обробники подій, навішені на поточний виділений елемент. Якщо ж встановити прапорець Ancestors, то до цього списку додадуться ще й обробники, навішені на будь-який елемент вище в дереві: саме вони потенційно можуть відреагувати на подію, пов’язану з поточним елементом. Для кожного обробника вказано місце в коді, де його оголошено. Крім того, є змога негайно видалити цей обробник, щоб перевірити, чи не він спричиняє певний баг, який ви зараз зневаджуєте.



У підвкладці DOM Breakpointsвідображено список усіх елементів DOM-дерева, на зміну яких браузер зупинятиме виконання скриптів (про налаштування таких точок зупинки див. трохи вище). Тут також можна тимчасово відключити ту або іншу зупинку.

У панелі Propertiesвиводять перелік усіх властивостей елемента як JavaScript-об’єкта, до того ж список розбивають за ланцюжком успадкування від властивостей Objectчерез Nodeй інші проміжні класи до властивостей самого елемента.



Панель Accessibilityпрезентує сторінку в такому вигляді, як її бачать пристрої для слабозрячих людей. Якщо виділити який-небудь елемент у лівій частині вкладки Elements, на цій панелі відобразиться інформація про нього. Водночас, якщо клацати на рядках-рівнях дерева доступності (Accessibility Tree), можна проглянути все це дерево. Утім, звичайно, тема доступності потребує для адекватного розкриття окремої повноцінної статті.

Вкладка Console

Основна функція цієї вкладки — інтерактивна взаємодія з браузером через введення в консоль JavaScript-команд й отримання у відповідь результатів, які вони повертають. Скажімо, у такий спосіб можна потестувати роботу певного фрагмента коду, просто вставивши його в консоль і натиснувши Enter. Інший сценарій використання — запуск глобально оголошеної функції або того чи іншого методу маніпуляції DOM. Наприклад, щоб додати в елемент footer текст, можна набрати й запустити в консолі команду document.getElementsByTagName('footer')[0].append('Bye!');якщо ж футер і так вибрано у вкладці Elements, то досить написати $0.append('Bye!'), бо Chrome автоматично підставить у змінну з назвою $0 поточний елемент.

Сюди ж, у вкладку Console, потраплятимуть діагностичні повідомлення, які виводить ваш код за допомогою команди console.log(…), а також спеціалізованих варіантів цієї команди:

  • console.dir(…) — на відміну від console.log(…), який намагається презентувати об’єкт у найзрозумілішій формі, dirвиводить його як дерево властивостей. Це може бути зручно, наприклад, коли ми аналізуємо DOM-елементи в ролі JavaScript-об’єктів.
  • console.trace() — виводить поточне дерево виклику (stack trace).
  • console.error(…) — виводить повідомлення як помилку, включаючи дерево виклику.
  • console.assert(expression, …) — виводить повідомлення про помилку, якщо вираз expressionмає значення false.
  • console.warn(…) — оформлює повідомлення як попередження.
  • console.count(label) — автоматичний лічильник: виводить мітку label і кількість разів, які console.countуже виводився з цією міткою, ураховуючи й поточний виклик.
  • console.time(label) / console.timeEnd(label) — засікає час між викликами time і timeEnd та виводить цей час.
  • console.table(…) — виводить масив об’єктів як таблицю.



За допомогою елементів у верхній частині консолі її можна налаштувати:



При натисканні на трикутник (кнопка зліва), відкриється панель, що дасть змогу відфільтрувати повідомлення в консолі — наприклад, показати лише помилки або попередження. Кнопка із зображеним на ній перекресленим колом натомість очистить всі повідомлення без змоги їх повернути. Далі розміщено випадний список, з якого можна вибрати контекст, в якому виконує команди консоль. Основний контекст — top — це сама сторінка, з якою ви працюєте. Однак у списку можуть бути й інші варіанти — по окремому контексту на кожен iframe, розміщений на сторінці, на кожен worker і навіть на розширення, що мають до сторінки доступ.

За допомогою наступної кнопки, на якій зображено око, можна створити «живий» вираз: його Chrome обчислює кілька разів на секунду й щоразу оновлює результат, який відображає на екрані. Це дає змогу в режимі реального часу стежити за змінами в значеннях, які вас цікавлять.

Поля, розміщені праворуч, теж призначені для фільтрування повідомлень у консолі. За допомогою останньої кнопки відкриваємо мініпанель з додатковими налаштуваннями консолі, такими як заборона очищувати консоль під час переходу на іншу сторінку й відключення «жадібного» обчислення — виведення в консоль значення виразу до натискання кнопки Enter.

Вкладка Sources

У цій вкладці можна переглянути, відкрити й навіть редагувати вихідні файли сайту. Для цього в лівій частині вкладки, на панелі Page, виберіть потрібний файл. Унесені в нього зміни будуть застосовані до сторінки в режимі реального часу: наприклад, якщо ви змінюєте стиль, то сторінка миттєво оновлює свій вигляд, а якщо змінюєте код, який виконує деяка кнопка, то після наступного натискання на цю кнопку браузер запустить уже нову версію коду. Щоправда, після перезавантаження сторінки всі внесені у файли зміни будуть втрачені. Щоб цього уникнути, вам потрібно підключити сторінку до локального диска, скориставшись панеллю Filesystem. Альтернативно, перемкнувшись на панель Overrides, ви зможете вибрати теку, файли з якої автоматично підмінятимуть ті, що завантажує сторінка.

Остання панель у лівій частині вкладки — Snippets — призначена для збереження й запуску фрагментів коду багаторазового використання. Прикладом подібного «сніпету» може бути завантаження на сторінку вашого звичного набору JS-бібліотек.

Нарешті, натиснувши в лівій частині вкладки кнопку з трьома крапками й вибравши пункт Open file, ви отримаєте доступ до поля швидкого пошуку файлів.



Проте, мабуть, найголовніше, що пропонує вкладка Sources — це можливість зневаджувати код. Спершу для цього потрібно зупинити виконання коду в тому місці, яке вас цікавить і яке ви хочете дослідити. Найпрямолінійніший спосіб виставити такий breakpoint — відкрити потрібний файл, знайти потрібний рядок і клацнути на його номері. Тоді номер підсвітиться синім кольором, а, коли виконання дійде до вибраного рядка, браузер призупинить виконання JS і підсвітить синім увесь рядок. Навіть якщо вихідний код сторінки мініфіковано й склеєно в один суцільний рядок, Chrome може його миттєво «розмініфікувати» й акуратно відформатувати. Для цього потрібно клацнути на спеціальній кнопці в нижній частині вкладки: там зображено фігурні дужки.



Панель у правій частині вкладки дає змогу керувати процесом зневадження. Кнопки зверху мають такі функції:

  • Продовжити виконання скриптів (якщо браузер перебуває в режимі зневадження) або зупинити виконання скриптів і перейти до зневадження (в іншому випадку). Також у режимі зневадження можна затиснути цю кнопку й вибрати один з двох додаткових варіантів:
    • продовжити виконання з ігноруванням точок зупину (breakpoints) протягом наступних 500 мілісекунд — наприклад, якщо ви не хочете зупинятися в тілі поточної функції;
    • перервати виконання поточного коду — наприклад, щоб вийти з нескінченного циклу.
  • Перейти до наступного рядка коду.
  • Перейти всередину виклику функції в поточному рядку коду. Якщо функцію викликають асинхронно, виконання перейде до коду за нею, але потім, коли настане час запуску функції, виконання зупиниться знову так, наче у функції виставлено breakpoint. Якщо поточний рядок передає повідомлення у фоновий потік або навпаки, виконання перейде у відповідний обробник.
  • Дочекатися виходу з поточної функції і продовжити зневадження.
  • Перейти всередину виклику функції в поточному рядку коду. Асинхронні функції й обробники повідомлень між потоками ігноруються.
  • Тимчасово деактивувати/поновити всі точки зупину.
  • Зупинятися на всіх винятках (exceptions).



Також панель містить такі блоки:

  • Блок Threads відображає, в якому потоці ми зараз перебуваємо, і дає змогу перемикатися між потоками.
  • Watch дає змогу відстежувати значення змінних і довільних заданих виразів. Зверніть увагу, що змінні беруться (і вирази обчислюються) відповідно до локального оточення коду в місці його зупинки.
  • Call Stack показує ланцюжок виклику функцій і методів (поточний рядок коду належить функції з верхнього рядка списку, яку викликала функція з наступного рядка, яку, своєю чергою, викликала функція з третього рядка списку і т. д.). Між функціями з цього ланцюжка можна перемикатися.
  • Блок Scope чимось нагадує Watch: у ньому структуровано виведено всі змінні, доступні з поточного контексту.
  • У блоці Breakpoints перераховано всі виставлені в DevTools точки зупину з можливістю тимчасово відключити або видалити кожну.
  • Блок XHR/fetch Breakpoints дає змогу встановити неформальну точку зупину на момент ініціювання будь-якого AJAX-запиту.
  • DOM Breakpoints показує всі елементи, на модифікацію яких встановлено зупинку (див. розділ про вкладку Elements), і, так само як і блок Breakpoints вище, дає змогу відключити відстеження кожної з таких подій.
  • Global Listeners має те саме призначення, що й панель Event Listeners вкладки Elements, але спеціалізується на подіях, навішених на window.
  • Блок Event Listener Breakpoints дає змогу встановити неформальні точки зупину на довільні з широкого класу подій: будь-які події, пов’язані з мишею, або окреме натискання лівою або правою кнопкою, подвійне натискання, вихід за межі елементів тощо; події клавіатури; події, пов’язані з анімаціями; різні етапи AJAX-завантажень; навішування і спрацьовування таймерів setTimeout/setInterval і чимало іншого.

Якщо ж панелей зліва й/або справа у вкладці Sources не видно, розкрийте їх, клацнувши на відповідних трикутниках зверху.


Вкладка Network

Тут відображено список усіх завантажених сторінкою ресурсів, а також діагностична інформація щодо кожного з них. У списку, правда, не буде завантажень, запущених до виклику інструментів розробника.



Спочатку з’ясуймо, які дані можна знайти в списку завантажень. У самій таблиці наявні зазвичай ім’я завантажуваного ресурсу, його тип, код статусу, повернений сервером, кількість трафіку, використаного на завантаження ресурсу, і час завантаження. Список можна відсортувати, клацнувши на заголовку відповідного стовпця таблиці. Якщо ж натиснути будь-де заголовок таблиці правою кнопкою миші, Chrome дасть змогу вибрати для відображення стовпці з додатковою інформацією, такою як, метод, домен, кількість cookies тощо.



Якщо правою кнопкою миші клацнути на елементі у списку завантажень, то можна скопіювати його адресу, експортувати в різні формати (зокрема, для використання в Postman) і заблокувати завантаження цього URL у майбутньому. Натомість клацання по завантаженню лівою кнопкою відкриє панель з великою кількістю додаткової інформації про нього. У загальному випадку це заголовки (headers) і параметри запиту, текст відповіді сервера (панель Response) та його перегляд у відформатованому вигляді (Preview), передані на сервер і назад cookies, детальний таймінг завантаження, а також, у разі сокетного з’єднання, інформація про передані в обидва боки повідомлення (панель Frames).



Тепер гляньмо на елементи керування, що розміщені у верхній частині вкладки. Перша кнопка запускає/зупиняє протоколювання завантажень, а друга — очищує список. Далі — відображення панелі фільтрації і швидкий пошук. Фільтрація дає змогу залишити в списку лише ті файли, чия назва містить певний підрядок або відповідає регулярному виразу, або ті, які мають певний тип (картинки, стилі, AJAX-запити тощо). Пошук дає змогу шукати не лише за назвою файлу, а й за контентом завантажень, наприклад, ви можете з’ясувати, на який саме запит клієнту повернуто той або інший елемент на сторінці, або виявити, які ресурси було віддано з певним заголовком (header).

Наступні два прапорці визначають, чи очищувати список завантажень під час переходу на іншу сторінку й чи дозволяти кешування, коли відкрито інструменти розробника (позначте Disable cache, якщо хочете симулювати завантаження для користувача, який ніколи раніше не заходив на сторінку).

Випадний список, що йде далі, призначений для тестування роботи сторінки в умовах повільного або навіть відсутнього Інтернету. Виберіть один з передналаштованих профілів швидкості або створіть власний.

Дві наступних кнопки дають змогу імпортувати й експортувати (тобто зберегти) весь список у спеціальному форматі HAR. Нарешті, кнопка налаштувань, що розміщена в правому куті, надає доступ до ще кількох корисних функцій:

  • Під час використання опції збільшеної висоти рядків браузер додатково показує для кожного завантаження повний шлях до файлу, а також його реальний розмір, без поправки на стиснення і додатковий обмін інформацією з сервером.
  • Панель Overview, за яку відповідає наступний прапорець, подає увесь процес завантаження сторінки хронологічно в графічній формі. Вибір мишею ділянки графіка автоматично фільтрує завантаження, обмежуючи список відповідним проміжком часу.



  • Наступний прапорець групує завантажувані ресурси відповідно до фрейма, якому вони належать.
  • Останній прапорець відкриває панель зі скриншотами, які браузер робить автоматично під час початкового завантаження сторінки. Це дає змогу проаналізувати, що саме та в якій послідовності бачить перед собою користувач, і може бути особливо корисним у комбінації із симулюванням повільного з’єднання (див. вище).

Вкладки Performance та Memory

За допомогою вкладки Performance можна профілювати сторінку, тобто відстежити, які чинники й ділянки коду роблять її повільною. Для запуску профілювання клацніть на одній з двох перших кнопок на панелі інструментів у верхній частині вкладки. Перша кнопка безпосередньо запускає збір інформації; зупинити його можна, натиснувши на кнопку Stop, що з’явиться. Натомість друга кнопка спершу перезавантажує сторінку, а потім автоматично починає збір інформації. Він припиняється, коли сторінку повністю завантажено.

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



Правіше на панелі містяться дві галочки, які для зручності приховують/показують на вкладці хронологічні скриншоти сторінки й графік використання пам’яті. Наступна кнопка викликає garbage collection — очищує зайняту пам’ять, яку насправді не використовують скрипти (це може знадобитися під час збору профілю). Остання кнопка відкриває панель налаштувань, за допомогою якої можна ввімкнути симуляцію повільного з’єднання з Інтернетом або слабкого процесора, а також виставити розширені параметри збору інформації.

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



Докладна інформація про вибраний проміжок буде доступна під графіком у формі окремих блоків, кожен з яких можна розгорнути. Наприклад, у блоці Main ви побачите діаграму виконання коду: по горизонталі йде час, а по вертикалі — ланцюжок виклику функцій. Блок Interactions містить протокол взаємодії користувача зі сторінкою (наприклад, натискання кнопок миші), Network — мережеві запити тощо.

Панель у нижній частині вікна інструментів розробника містить чотири додаткові вкладки. Вкладка Summary пропонує легенду до кольорів графіка й секторну діаграму часу виконання за вибраний проміжок часу.



Вкладка Bottom-up сортує всі функції, що викликані у вибраний період, за часом виконання власного коду (тобто без часу виконання «дочірніх» функцій) — це дає змогу швидко знайти конкретні рядки коду, що виконуються повільно. Для кожної функції можна переглянути ланцюжок її виклику. Водночас вкладка Call Tree сортує всі виконані браузером дії за загальним часом виконання і дає змогу розгортати їх, а також переглянути все піддерево викликів, яке вас зацікавить. Нарешті, у вкладці Event Log у хронологічному порядку розміщено всі події, що їх браузер зафіксував протягом вибраного періоду часу.

Вкладка інструментів Memory розробника дає змогу ліпше розібратися з тим, як на сторінці виділяється пам’ять під JavaScript.



Передусім у вкладці запропоновано вибрати один з трьох видів профілювання пам’яті:

  • Heap snapshot показує, які об’єкти займають скільки пам’яті саме в цей момент.
  • Allocation instrumentation on timeline будує хронологічний графік виділення пам’яті і дає змогу проаналізувати список об’єктів, створений протягом вибраного на графіку періоду часу.
  • Allocation sampling виводить інформацію про виділену пам’ять, розподілену за окремими функціями, які її споживали.

Для запуску будь-якого з цих видів профілювання слід натиснути на першу кнопку на мініпанелі інструментів у лівому верхньому куті вкладки; для зупинки останніх двох типів профілювання треба натиснути на цю кнопку ще раз. Ця мініпанель також містить кнопку Collect, про призначення якої ми говорили трохи вище.

Вкладки Application, Security й Audits

Вкладка Applications містить кілька окремих розділів:

  • Manifest — показує діагностичну інформацію про маніфест сторінки як веб-застосунку.
  • Service Workers — відображає список скриптів однойменного типу й дає змогу керувати ними.
  • Clear Storage — дає змогу очищувати збережений і закешований вміст сайту.
  • Local Storage, Session Storage і Cookies — надають інструменти для перегляду, редагування й додавання ключів/значень у відповідні сховища даних.



  • IndexedDB і Web SQL — дають змогу переглядати інформацію, збережену в локальних базах даних.
  • Cache Storage й Application Cache — відображають список програмно закешованих ресурсів.
  • Background Fetch і Background Sync — відображають події фонового завантаження й надсилання даних на сервер.
  • Notifications і Push Messaging — відображають події, пов’язані з пуш-повідомленнями та їхнім відображенням користувачеві.
  • Frames — ще один спосіб переглянути файли, завантажувані сторінкою; тут їх розподілено за типом.

Вкладка Security інструментів розробника надає інформацію, пов’язану з HTTPS: показує список усіх незахищених доменів, з яких вантажить ресурси сторінка (якщо такі є), а також дає змогу переглянути інформацію про SSL-сертифікат.



Вкладка Audits — проведення автоматизованої діагностики сторінки — як настільної, так і мобільної її версії — за цілою низкою параметрів:

  • Швидкодія (прапорець Performance).
  • Поведінка сторінки як незалежного застосунку (прапорець Progressive Web App).
  • Наявність або відсутність у коді потенційно проблемних моментів, наприклад використання застарілих API (прапорець Best practices).
  • Зручність використання сторінки для людей з вадами зору (прапорець Accessibility).
  • Оптимізованість сторінки для індексації пошуковими рушіями (прапорець SEO).

Після проведення тестів браузер виведе результати перевірки сторінки, а також набір порад щодо усунення виявлених недоліків.


Додаткові панелі

Додаткові панелі можна відкрити, скориставшись пунктом More tools меню інструментів розробника (див. розділ «Виклик і налаштування» вище). Тут є:

  • Додаткова консоль. Стане в пригоді, якщо ви захочете попрацювати водночас і з консоллю, і з однією з інших основних вкладок інструментів розробника.
  • Animations. Виводить список усіх анімацій, що запускаються на сторінці, поки панель відкрита, і розумно їх групує, спираючись на таймінг. Для кожної анімації можна переглянути її вихідний код та підправити параметри; також повторно відтворити анімацію, зокрема в сповільненому темпі.
  • Changes. Відображає зміни в коді сайту, внесені безпосередньо в браузер для тестування сторінки й відсутні в реальному вихідному коді.



  • Coverage. Показує статистику того, яка частина завантаженого JS- і CSS-коду реально використовується на сторінці. Дає змогу зробити детальний порядко́вий аналіз і в такий спосіб виявити й видалити зайвий код.
  • Layers. Дає змогу переглянути 3D-модель сторінки й проаналізувати її розбивку на спеціальні шари, в які браузер групує різні елементи й промальовує один над одним.
  • Performance monitor. Відображає навантаження сторінки на процесор, кількість пам’яті, яку з’їдає JavaScript, кількість DOM-елементів і навішених обробників подій, частоту перерахунку стилів, оновлення розмітки, а також графіки зміни цих параметрів з часом.
  • Remote devices. Дає змогу підключати до браузера Android-пристрої і зневаджувати роботу сторінки на такому пристрої просто на комп’ютері.
  • Rendering. Надає інструменти відстеження ефективності рендерингу сторінки — наприклад, діагностику FPS і підсвічування перемальовуваних ділянок на екрані. Також тут можна симулювати режим друку сторінки.
  • Request blocking. Список усіх заблокованих для завантаження URL з можливістю його очистити або додати до списку нові адреси.
  • Sensors. Вкладка дає «підробити» показники умовних сенсорів, які браузер надсилає сторінці. Наприклад, тут можна змінити місце перебування користувача й орієнтацію пристрою.
  • WebAudio. Виводить діагностичну інформацію щодо однойменної технології.

Мобільне подання

Закінчимо огляд можливостей інструментів розробника, розказавши (або нагадавши) читачеві про функцію мобільного подання, за яку відповідає кнопка у верхній частині вікна DevTools із зображеними на ній ґаджетами:



Після натискання кнопки браузер перейде у спеціальний режим, де ви можете вказати конкретну ширину й висоту умовного пристрою — їх установить для сторінки браузер. Окрім того, можна просто вибрати одну із заздалегідь заданих моделей телефонів або планшетів, під які підлаштується Chrome.



Браузер емулюватиме не лише розмір екрана, а й спеціальний рядок ідентифікації пристрою (user agent string), який він передає вашому коду. Подібно до мобільного пристрою оброблятиметься також клацання мишею — як «дотик» до сторінки. Вийти з цього особливого режиму можна, повторно клацнувши кнопку зверху DevTools.

Підсумок

Отже, інструменти розробника Chrome пропонують цілий набір способів перевірки й оптимізації коду, а також боротьби з помилками в ньому. Сподіваюся, ви взяли собі на замітку ті чи інші з цих можливостей, і наступного разу, коли візьметеся за веб-розробку, ваша робота виявиться ще ефективнішою.

За детальною документацією по функціоналу, пов’язаному з DevTools (зокрема й такому, якого на момент написання статті там ще не було), ви завжди можете зайти на присвячену їм сторінку документації.

5 книг, которые научат мыслить системно, от Кирилла Белобородько, Software Engineering Manager в EPAM

$
0
0

От редакции: в рубрике DOU Booksучастники сообщества рассказывают о пяти любимых книгах — тех, которые меняют мировоззрение и могут быть полезны читателям-коллегам.

[Об авторе: Кирилл Белобородько сотрудничает с ЕРАМ в качестве Software Engineering Manager. Увлекается менеджментом и развитием на стыке инженерных компетенций, управленческих навыков и soft skills. Ведет один из потоков клуба бизнес-литературы ReadArea в Харькове, где обсуждает с участниками прочитанные книги о менеджменте и лидерстве]

Шесть-семь лет назад я почувствовал необходимость в систематизации навыков управления. У меня было интуитивное понимание, как решать те или иные проблемы, с которыми сталкивается менеджер, но теории не хватало. «7 навыков высокоэффективных людей» Стивена Кови стала первой книгой, которая заложила фундаментальные основы (хотя она скорее о личностном росте, но бизнес-составляющая в ней тоже есть). С тех пор я прочитал 176 книг.

Для выбора литературы у меня есть определенная система. Раз в год, в свой день рождения, я составляю список из 100 книг: по 20 изданий в категориях «Бизнес-литература», «Управленческая литература», «Техническая литература», «Художественная литература», а также «Литература о семье и здоровье». Все эти книги я стараюсь читать параллельно. По мере продвижения по спискам я вижу, какая сфера жизни и развития получает больше внимания, а на какую следует сделать больший акцент. Это помогает сохранять баланс.

Я читаю в четыре приема каждый день. Утром, до ухода на работу, успеваю пройти 20-25страниц бумажной книги. В течение дня, если у меня есть какие-то административные, сугубо механические задачи, слушаю литературу в аудиоформате. Во время обеда я читаю электронную книгу. Вечером дома — снова бумажную.

Эта подборка литературы о системном мышлении родилась спонтанно, но я быстро понял, по какому принципу объединяются книги. Одна из них знакомит читателя с базовыми понятиями и фундаментальными подходами к системному мышлению. Две другие рассказывают о математическом взгляде на вопрос (то есть о работе от частного к общему — так, методом индукции, решают задачи разработчики); еще две — о философском подходе к систематизации мышления (иными словами, о дедукции, когда человек считает происходящее лишь симптомом и фокусируется на решении глобальной, комплексной задачи — такой метод близок опытным руководителям).

Итак, книги, которые научат мыслить системно.


Иан Макдермотт и Джозеф О’Коннор «Искусство системного мышления»

В оригинале — The Art of Systems Thinking: Essential Skills for Creativity and Problem Solving by Joseph O’Connor

В украинском переводе — «Системне мислення. Пошук неординарних творчих рішень»Ієна Макдермотта, Джозефа О’Конора

базовая книга

Эту книгу часто критикуют за размытость целевой аудитории: мол, она достаточно сложна для людей с гуманитарным складом ума и слишком очевидна для тех, кому близки точные и естественные науки. Однако я все равно включаю ее в свой рейтинг, потому что хороший менеджер, на мой взгляд, как раз должен сочетать технический подход и гуманитарные компетенции. Это особенно актуально в нашей сфере. Так что я считаю, что ее целевая аудитория — это как раз те, кто хочет объединить инженерные навыки с управленческими.

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

Нассим Николас Талеб «Одураченные случайностью»

В оригинале — Fooled by Randomness: The Hidden Role of Chance in Life and in the Markets by Nassim Nicholas Taleb

книга с математическим подходом

У Талеба в «Антихрупкости» приводится история из «Одураченных случайностью». Она-то меня и зацепила. Это рассказ о том, как автор общался с инструктором военного училища, который обучал пилотов. Он считал, что в управлении людьми похвала не работает, а эффективна только критика. Вывод инструктора строился вот на чем: если пилоты после успешно выполненного упражнения получали поощрение, то в следующий раз показывали результаты хуже. Если же после неудач их критиковали, то новые попытки были лучше.

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

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

Отмечу, что читать Талеба непросто, у него много философских примеров и сложных объяснений из жизни. Его книги будут интересны достаточно опытным менеджерам, но равнодушными не оставят никого — это точно.

Авинаш Диксит и Барри Нейлбафф «Теория игр»

В оригинале — The Art of Strategy: A Game Theorist’s Guide to Success in Business and Life by Avinash K. Dixit, Barry J. Nalebuff

книга с философским подходом

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

Известный классический пример — дилемма заключенных. Полицейские поймали двух грабителей и допрашивают их порознь. Каждому предлагают сделку: выдай подельника и получишь год заключения, а он — десять. Если будешь сохранять молчание, а напарник тебя очернит, десять лет за решеткой проведешь ты. Признаетесь оба — получите по два года заключения. Чаще всего в такой ситуации люди не доверяют друг другу, каждый решает рассказать о соучастнике, и в итоге теряют оба. Такие варианты парадигм и рассматривает теория игр.

Майкл Микалко «Рисовый штурм»

В оригинале — Thinkertoys: A Handbook of Creative-Thinking Techniques by Michael Michalko

В украинском переводе — «21 спосіб мислити креативно»Майкла Міхалка

книга с философским подходом

Я начал читать эту книгу, потому что слышал о ней много позитивных отзывов. «Рисовый штурм» учит менеджеров генерировать новые идеи. В сфере разработки ПО этот навык важен, потому что часто руководителями становятся те, кто был лучшим инженером и решал задачи путем индукции (то есть от частного к общему). Креатив для таких людей — это достаточно неизведанная сфера, которой надо учиться. Эта книга — о практике, она содержит много примеров и упражнений на развитие творческого мышления у менеджеров. Выпущено даже дополнение с заданиями.

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

Барбара Оакли «Думай как математик»

В оригинале — A Mind For Numbers: How to Excel at Math and Science by Barbara Oakley

В украинском переводе — «Навчитися вчитися. Як запустити свій мозок на повну»Барбари Оклі

книга с математическим подходом

Автор рассказывает о собственном переходе от гуманитарных наук к естественным и попытках разбираться в вопросах по схеме «от частного к общему». Это своеобразный сборник методологий, который обычно используют люди с математическим складом ума, чтобы анализировать события и находить взаимосвязи. Мне, как человеку с экономическим образованием, во многом склонному к естественным наукам, было интересно. Конечно, инженерам и менеджерам с техническим прошлым многие вещи будут знакомы, но все равно любопытно и полезно посмотреть на людей с иной точкой зрения и непохожим образом мышления.


Кстати, о математическом и философском подходах. Недавно ради интереса я проходил курс Physics Foundations от Khan Academy, одна из первых лекцийкоторого была посвящена взаимосвязи наук. Речь шла о том, что на базе математики (царицы наук, как о ней обычно говорят) строится физика, далее — химия, а венчает эту пирамиду биология. Меня увлекла эта логическая цепочка, пришла мысль, что то же самое применимо и к ветви гуманитарных наук. В ней царицей — базой — является философия. И на мой взгляд, чем профессиональнее и опытнее становится менеджер, тем чаще он преодолевает вызовы именно с позиции этой науки. Он задает себе все более сложные, неоднозначные вопросы. Так происходит его эволюция.

«Это все не имена и цифры, это люди»: как становятся Customer Engagement Manager

$
0
0

Меня зовут Иван Яровенко, начал карьеру с позиции дизайнера еще в далеких 2000-х,и сейчас занимаю позиции Director, Client Engagement, Medical Technology в HQ GlobalLogic. Когда я только начинал работать, я не знал, как мне лучше построить карьеру и не было никого, кто мог бы мне что-то подсказать или посоветовать. Пришлось набивать шишки, но благодаря этому я узнал, какие есть подводные камни и что действительно важно в работе с людьми. Надеюсь, мой опыт пригодится тем, кто ищет себя и хотел бы развиваться в направлении Customer Engagement. Для меня отправной точкой был именно дизайн: я опираюсь на этот опыт, знание принципов дизайна помогает мне делать всю остальную работу. Эту часть истории я бы и хотел сейчас рассказать.

Для начала нужен личный опыт

Моя история началась с того, что классе в восьмом тетя подарила мне диск со второй или третьей версией фотошопа. Поначалу я с ним просто баловался: вставлял голову тогдашнего президента на фотографию Black Sabbath и прочее. Чуть позже я разжился книжкой по HTML, что мгновенно сделало меня «невероятным экспертом», которому поручили создать сайт родной школы.

Моя первая настоящая работа была далека от информационных технологий. Я гордо назывался дизайнером, фотографировал микроволновки, делал плакаты, брошюры, этикетки со скидками. Очень ценный опыт, кстати говоря.

Мне кажется, что именно опыт — основа UX-дизайна. Ведь для того, чтобы дизайнить чей-то experience, нужно иметь свой.У молодых специалистов часто нет опыта ни в пользовании софтом, ни в разработке; они в целом не так много видели в жизни. Такие дизайнеры торопятся уйти за модной аббревиатурой, не зная основ.

В начале 2000-хсчиталось,что дизайнер — это человек, который может сделать все что угодно, отличное от математики: и кино, и озвучку, и статью написать. Термина User Experience Designer не существовало. Эта мультизадачность помогла мне многому научиться.

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

Нынешним дизайнерам-новичкам я бы посоветовал освоить визуальную графику. Многие говорят: «Я UX-дизайнер, делаю только UX, рисовать не умею». Знаю таких людей, но не верю в это. Хороший дизайнер должен уметь делать графику, хотя бы в теории. Знать, чем один пиксель отличается от трех, как цвета отображаются на разных устройствах, овладеть основами типографики. Люди, приходя в дизайн, узнают эти вещи уже потом. А лучше бы сначала.

Чтобы расти дальше, нужно завоевать своего клиента

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

Чтобы стать Customer Engagement Manager, совершенно необязательно входить в какую-то бюрократическую структуру. Начинать надо с низов, а не сразу занимать высокую позицию. Если человека поставить и сказать: «Теперь ты Customer Engagement Manager, вот тебе все инструменты, пожалуйста, вперед» — ничего не будет. Это как с музыкантами, люди говорят: «Дайте мне классный инструмент, классного продюсера, промоутера, я буду звездой». Нет. Ты сначала покажи, что ты звезда, а потом это все появится.

Нужно научиться общаться

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

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

Навыки дизайна пригодятся и в других сферах, особенно в работе с клиентами

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

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

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

Customer Engagement Manager: особенности сферы

Сейчас я занимаюсь менеджментом клиентов в США. Разработка предложений и собственно общение с заказчиками, с командой — большая часть моей работы. Это означает, что я всегда меж двух огней: необходимо учитывать и запросы клиента, и загрузку, возможности команды.

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

Что такое карьерный рост у нас и за границей

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

Многие наши соотечественники до сих пор так думают, и это проблема, я считаю. Они не хотят становиться хорошими разработчиками, чтобы быть отличными специалистами. У всех начинающих, особенно тех, кто помоложе, план выглядит примерно так: приду на работу, буду Junior 1 год, потом — Middle 1,5 года, а лучше год, затем — Senior 2 года, лидом 2 года, и тогда уже стану менеджером. Люди стремятся к осуществлению этого плана, а не к тому, чтобы стать лучшими специалистами. А нам нужны хорошие менеджеры, которых не так уж много.

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

Советы тем, кто хочет стать Сustomer Engagement Manager

Разберитесь в особенностях сферы клиента

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

Изучите культурную среду собеседника

Самое важное — знать особенности культурной среды, в которой мы находимся. Культурные отсылки — это о примерах, метафорах, о том, как можно шутить, говорить, на какие фильмы и музыку ссылаться и т. д. Если убрать из разговора эту составляющую, то общение становится сухим и сводится к обмену фактами.

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

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

Многие люди, которые выросли в 90-е,как и я, абсолютно не представляют, откуда они вообще, что такое украинская культура, где это, почему это важно.

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

Умейте поддержать разговор

К примеру, один из ваших клиентов внезапно захочет поговорить о гольфе. Вот надо ему. А вы или знаете, что сказать, или нет. А другой клиент хочет обсудить, как летают дроны. И точно так же: вы или знаете, что ответить, или нет. Но вы остаетесь одним человеком, а клиентов у вас 11.

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

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

Старайтесь по максимуму

Ваши ошибки стоят другим людям работы. Например, клиент пришел и говорит, что ему нужно 15 человек на такой-то проект, и просит подготовить ему предложение. Мы идем готовим и одновременно ищем людей. Выглядит так, будто все уже сделано. Однако приходим мы показать предложение, а оно плохое. Кто-то что-то не услышал и сделал не так, как клиент просил. Это наша ошибка. А эти 15 человек теряют работу.

Осознавайте ответственность

Меня очень расстраивает, когда я что-то пообещал, но из-за обстоятельств не смог выполнить обещание. В результате другие люди тоже вынуждены менять свои планы, кто-то больше, кто-то меньше. Я чувствую эту ответственность каждый день. И порекомендовал бы людям осознавать то, что они делают. Я не говорю, что не надо идти в эту сферу — просто знайте, что это вообще не о вас. Все, что вы делаете, не имеет к вам никакого отношения. Это имеет отношение к людям и их труду, это не просто имена и цифры.

Выводы

Customer Engagement Manager — это в первую очередь о том, как находить общий язык с клиентом и при этом не терять связи со своей командой. Чтобы стать таким специалистом, нужно проявлять инициативу — говорить с руководством, развивать навыки коммуникации, искать интересные для себя и компании проекты. Таким образом вы сможете не только быть полезным команде, но и открывать для себя новые возможности. Причем часто такие, к которым мало у кого есть доступ. Конечно, со знанием приходит и ответственность. В случае Customer Engagement Manager это ответственность за других людей. Ваши договоренности с клиентом непосредственно влияют на ваших коллег, поскольку это тоже их работа.

Как в DB Best создали BI-проект статистики игр в покер

$
0
0

В рубрике DOU Labsмы приглашаем IT-компании делиться опытом собственных интересных разработок и внутренних технологических инициатив. Вопросы и заявки на участие присылайте на editors@dou.ua.

Привет, меня зовут Александр, и я BI-разработчик в компании DB Best. В нашей команде pet-проект родился сам собой, и сейчас он достаточно окреп, чтобы рассказать о нем. Мы создали с помощью Power BI-проект, который содержит статистику игр в покер за несколько лет, позволяет сформировать удобно организованный и красивый отчет о любой партии и любом игроке и на основании этих данных проанализировать и предсказать поведение соперников.

Идея

В 2017 году в DB Best организовался клуб игроков в покер. Совесть и руководство компании, естественно, не позволили бы участникам играть на деньги, поэтому прошедшие игры забывались, и скоро играть ради самого процесса стало не интересно. Так появилась идея вести статистику и отслеживать успехи игроков.

Для записи результатов игр мы завели Google-док, который скоро оброс огромным количеством страниц и дополнительных отчетов. Графиков и таблиц было много, организовать и проанализировать их в Google-документе становилось все сложнее.

Тогда было решено создать локальный BI-проект, чтобы организовать все данные и иметь возможность строить мультимедийные статистики. В качестве альтернативы выбрали Power BI, у которого было три очевидных плюса: мы работаем с этим софтом каждый день, он бесплатный в десктопной версии и использует тот же язык программирования, что и таблицы Excel, поэтому перенести информацию было несложно.

Реализация

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

Мы не играем на деньги, но для того, чтобы играть было интереснее, решили для подсчета общей статистики представить, что каждый из игроков поставил на кон 100 долларов. Основываясь на этой легенде, мы строим все репорты.

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

Примерами простых кейсов может быть наш расчет общих достижений — Overall Achievements, для которого достаточно было использовать имеющийся функционал настройки data bars.

Вот так выглядит пятерка самых успешных игроков этого года и их показатели:

А вот как с технической стороны работает «ачивка» нескольких побед подряд:

Вычисляем порядковый номер игры (Latest Game), затем определяем предыдущий (PreLatestGame), учитывая, что игрок не обязательно приходит два раза подряд, а может делать перерывы, и вычисляем место, которое он занял в прошлый раз (Prev Place).

PreLatestGame = 
VAR vPlayer = Games[Player]
VAR vGameNumber = Games[Latest Game]
RETURN
MINX(
FILTER( Games,
Games[Player] = vPlayer&& Games[Latest Game] > vGameNumber
),
Games[Latest Game]
) 

Prev Place = 
VAR vPlayer = Games[Player]
VAR vGameNumber = Games[PreLatestGame]
RETURN
MINX(
FILTER( Games,
Games[Player] = vPlayer
&& Games[Latest Game] = vGameNumber
),
Games[Place]

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

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

CMoney = 
IF (
COUNTROWS ( Games ) > 0,
CALCULATE (
[MMoney],
FILTER (
ALL ( 'Date'[Game Date] ),
'Date'[Game Date] <= MAX ( ( 'Date'[Game Date] ) )
)
),
BLANK ()

Репорт показывает статистику на любую дату или за любой период времени, так что можно бахвалиться своими победами, как Олимпиадой-1980.

Сложности

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

Изначально мы не предусмотрели, что, когда два человека одновременно выбивают кого-то из игры, приз должен делиться поровну. Аналогично, если по каким-то причинам игрок не доигрывает партию, а тот, кто играет за него, побеждает, все призы и победные очки должны делиться пополам. Для этих случаев мы придумали нетривиальное решение и применили DAX-формулы с созданием промежуточных агрегирующих in-memory-таблиц. Чтобы добавить этот функционал, нам пришлось изменить модель данных.

Для ведения дополнительных страниц статистики об успехах игрока в течение месяца, года или игрового сезона использовались calculated tables. Например, для определения лучших игроков месяца (по таким критериям, как доход, призовые места и посещение игр в целом):

Awards Monthly = SUMMARIZE(Games,'Date'[Game Month],Games[Player],"Games",COUNT(Games[GameDate]),"Pts",[Pts],"Money",[Money])

Поверх этой таблицы мы вычисляем ранг игрока в пределах месяца:

Money Rank = var GM = 'Awards Monthly'[Game Month]
return RANKX(FILTER(ALL('Awards Monthly'),'Awards Monthly'[Game Month]=GM),'Awards Monthly'[Money])

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

Или, например, можно определить беспрерывную серию игр.

В основной таблице определяется номер игры, с которой началась эта серия:

Game Sequence Started = 
VAR vPlayer = Games[Player]
VAR vGameNumber = Games[Latest Game]
RETURN
MINX(FILTER(Games,
Games[Player] = vPlayer&& Games[Game Seq Reset] = 1&& Games[Latest Game] >= vGameNumber)
,Games[Latest Game])

В вычисляемой таблице:

LatestPlayerGame = SUMMARIZE(Games,Games[Player],"Max Game Date",MAX(Games[GameDate]),"Latest Game",MIN(Games[Latest Game]))

Колонка с номером игры в беспрерывной серии:

Latest Serie Started = MINX(Games,CALCULATE(VALUES(Games[Game Sequence Started]),FILTER(Games,Games[Player]=LatestPlayerGame[Player] && Games[GameDate]=LatestPlayerGame[Max Game Date])))

Latest Serie = IF(LatestPlayerGame[Latest Game]<>1,BLANK(),[Latest Serie Started] - LatestPlayerGame[Latest Game] + 1)

Latest Serie — это и есть количество беспрерывно сыгранных турниров. Этот показатель также делится на максимально успешную для игрока за все время ведения статистики и «текущую» серию.

Например, игрок Ilya участвовал в 22 играх, не пропустив ни одного раза, и сыграл без перерывов в 6 последних игр.

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

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

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

Результат

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

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

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

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

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

Viewing all 2421 articles
Browse latest View live