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




Когда Брендан Эйх создал самую первую версию JavaScript для Netscape Navigator 2.0 всего за десять дней, вряд ли он ожидал, в какой степени Slack Desktop App будет использовать его изобретение. Мы используем только кодовую базу JavaScript для многопоточного десктопного приложения, которое постоянно взаимодействует с нативным кодом и работает под Windows, macOS и Linux.

Управлять большими кодовыми базами JavaScript непросто. Всякий раз, когда мы мимоходом передаём объекты из JavaScript браузера Chrome в Objective-C, чтобы просто получить обратный вызов через другой поток на Node.js, нужна гарантия, что все кусочки складываются вместе. В десктопном мире маленькая ошибка может привести к сбою приложения. С этой целью мы внедрили TypeScript (статически типизированное надмножество JavaScript) и быстро поняли, как жить без волнений и с любовью к компилятору. И не только мы: опрос разработчиков на Stack Overflow показывает, что TypeScript является третьей самой любимой технологией программирования. Учитывая, насколько быстро статическая проверка типов набирает ход, мы хотим поделиться нашим опытом и методиками.

Статический анализ приходит на помощь


В прошлом мы использовали JSDoc, чтобы документировать сигнатуры наших функций. Мы в комментариях объясняли цель и способ правильного использования классов, функций и переменных. Такой способ не был лишён своих проблем. Глядя на сам код трудно понять, как разрешится промис JavaScript. Вам придётся довериться, что автор кода всё правильно задокументировал, а тот, кто позже вносил правки в код, корректно обновил документацию. В сложных системах с бесчисленными модулями и зависимостями очень легко сломать функцию даже не открывая файл, в котором она записана.

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

Статическая проверка типов понимает, что Math.random() возвращает число, которое не содержит строковый метод toLowerCase().



Чтобы более явно проявить намерения, пользователь такого контролёра типов может помочь системе, вручную объявляя эти типы — чтобы сообщить и людям, и машине, как должна себя вести программа. Код ниже определяет интерфейс для объекта “user” и метод, который предположительно должен получить возраст пользователя. Статическая проверка типов способна провести анализ такого кода и предупредить о типичных человеческих ошибках, таких как ожидание постоянного присутствия свойства, которое может быть undefined.



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



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

Портирование кодовой базы Slack Desktop на TypeScript


Мы решили использовать Microsoft TypeScript, где сочетается статический анализ с компилятором. Современный JavaScript — это валидный TypeScript, то есть вы можете использовать TypeScript не меняя ни единой строки кода. Это позволило нам реализовать «постепенное типирование», включив компилятор и статический анализ на ранней стадии, не приостанавливая работу над критическими багами или новыми функциями.

На практике включение анализа и компилятора без изменения кода означает, что TypeScript немедленно попытается понять ваш код. Он использует встроенные типы и определения типов, доступные для сторонних зависимостей, чтобы проанализировать поток кода, указывая на трудноуловимые ошибки, которые раньше никто не заметил. Если TypeScript не может понять ваш код, он просто предположит специальный тип под названием any, и пойдёт дальше.

Наш изначальный план состоял в том, чтобы медленно портировать файлы один за другим, расширяя стандартный JavaScript более конкретными определения типов там, где это возможно — добавляя интерфейсы, определяя методы классов как приватные или публичные и объявляя перечисления (enum). По ходу дела мы сделали два неожиданных открытия:

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

Во-вторых, мы недооценили, насколько здесь мощная интеграция с редактором. Благодаря лингвистическому сервису TypeScript редакторы с автодополнением могут поддерживать программирование с контекстными подсказками. TypeScript понимает, какие свойства и методы доступны для определённых объектов, так что редактор теперь тоже понимает. Система автодополнения, которая подсказывает только слова из текущего документа, после этого кажется варварской. Осталось в прошлом время, когда мы опять искали в Google, какие события доступны для Electron BrowserWindow. Есть плагины для Atom, Visual Studio Code, Sublime и почти всех остальных редакторов. Возможность проверять код прямо в редакторе немедленно повысила нашу производительность.



Заглядывая в будущее и думая о поддержке кода, мы ценим экосистему вокруг TypeScript. Для нас как активных пользователей экосистемы React и Node/npm огромным плюсом является доступность определений типов для сторонних библиотек. Многие из импортированных библиотек уже совместимы с TypeScript. Если определения не поставляются с самим модулем, их вероятно можно найти в фантастическом проекте DefinitelyTyped. Например, React не поставляется с определениями типов, но они устанавливаются простой командой npm install @types/react без необходимости в дальнейшей конфигурации.

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

Коммиты с уверенностью


Чтобы улучшить читабельность и удобство обслуживания, весь код в среде разработки автоматически проверяется TSLint перед коммитом — это значит, что перед добавлением изменений в Git код сначала проверяется на предмет некорректных выражений по нашим правилам. Мы не разрешаем использование опции implicit any, то есть во всём коде Slack Desktop должен быть явно указан тип, если TypeScript не может автоматически определить его.

Когда приходит время отправлять изменения в ветку, Git сначала пропускает всю кодовую базу через компилятор TypeScript, который анализирует весь код на структурные и функциональные ошибки и переводит современные функции вроде async/await в ES2016-совместимый код. Ко времени открытия пулл-реквеста у нас уже есть уверенность, что в коде правильные структурные зависимости.

Это может показаться страшным


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

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

Отдать обратно


В Slack мы стремимся быть порядочными участниками сообщества open-source. В конце концов, мы хотим сделать TypeScript проще для других разработчиков. Если мы видим пробелы, то стараемся заполнить их.

Прежде всего, собственный пакет electron-compile от Slack позволяет разработчикам Electron Apps писать на TypeScript, не беспокоясь о компиляции. RxJS, библиотека Reactive Extension, которая активно используется в Slack, Netflix, GitHub и многих других компаниях, перешла на TypeScript при содействии Slack. Много маленьких библиотек, написанных нашими разработчиками, постепенно получают поддержку TypeScript (как spawn-rx, electron-spellchecker, electron-remote, electron-notification-state и electron-windows-notifications).

Чтобы начать работу с TypeScript, смотрите официальное руководство. Если хотите узнать, как на практике выглядят маленькие портированные проекты, взгляните на порт spawn-rx. Если желаете написать свои первые строчки на TypeScript для приложения Electron, используйте великолепный electron-forge, в котором реализован electron-compile и поддержка TypeScript прямо из коробки — он даже поставляется с отличным шаблоном React/TypeScript, эту архитектуру очень любит наша группа разработки Slack Desktop. Если совмещение современных веб-технологий с нативным кодом для разработки кроссплатформенных приложений вам кажется увлекательным делом, приглашаем к нам на работу!
Поделиться с друзьями
-->

Комментарии (27)


  1. Alexeyco
    19.04.2017 12:56
    +3

    Интересно, когда же случится пришествие Webassembly. Бравурные саксесс-стори типа «как мы закостылили закостыленное и перестали так сильно хромать» — это, конечно, модно. Но хотелось бы не костылей, а именно что решений. Эти пресловутые 10 дней из жизни Брендана Айка, аукаются до сих пор. Бардак страшнейший.


    1. khim
      19.04.2017 22:46

      Интересно, когда же случится пришествие Webassembly.
      А это ещё зачем на десктопе-то? Там, как бы и так можно и C++ и Fortran использовать…


      1. AlexanderG
        23.04.2017 20:35

        Чтобы замкнуть круг истории, конечно же. Да и еще жива память о NaCl, ActiveX и Java-апплетах в браузере…


  1. Crandel
    19.04.2017 13:05
    +2

    Вот чем вам Qt не угодил? Вот почему чат должен сжирать полтора гига оперативной памяти. На ноуте с 8 гигами невозможно работать, слак + хром + pycharm = тормоза


    1. Alexeyco
      19.04.2017 13:13
      +3

      Потому, что Qt — это банально. О чем тогда писать статьи. Фоткать офис и писать обзор на кофеварку?


      1. tnc4401
        19.04.2017 13:31

        Я может не разбираюсь… А на Qt можно обновлять приложение и править баги быстро и незаметно для пользователя?


        1. Alexeyco
          19.04.2017 17:21

          Править баги так, чтобы это не было заметно, можно в любом языке.


    1. immaculate
      19.04.2017 13:50
      +4

      Да, и вообще, slack — это фактически irc с web-клиентом. Для irc написан миллион клиентов, в том числе на C для консоли, которые занимают менее 1 Мб RAM.


      Только slack умудрился несмотря на все тайпскрипты раздуть простой irc-клиент до нескольких гигабайт.


      Воистину, js — это новый PHP.


      1. Alexeyco
        19.04.2017 16:12

        Любой массово используемый язык будет PHP. Если, конечно, завтра все не побегут учить хаскель или тикль/ток. Чтобы хейтить их, надо будет в них ооочень глубоко погрузиться.


    1. FINTER
      20.04.2017 04:03
      +3

      Порог входа выше же. Надо С++ знать ну или хотя бы Python.

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

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

      Помнится лет 10 назад я хейтил Java. Медленная дескать тормозная, память кушает. Думал, что хуже быть не может. Как же я ошибался…


      1. immaculate
        20.04.2017 07:35
        +1

        А самое главное, что пока хороший продукт будет пилиться, он уже выйдет из моды. Сейчас время быстрых хайпов. «Оп-па, смотрите, у нас продукт на NoSQL!», «А у нас ИИ», «А у нас Blockchain!», «А у нас Node.js».


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


        Например, уверен, что через 10 лет никакого Твиттера уже не будет, т.к. это абсолютно бессмыссленная соц. сеть, и они сами уже много лет не могут придумать как ее позиционировать. Слака скорее всего тоже не будет, его заменит, что-то похожее, но более модное.


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


    1. ankh1989
      20.04.2017 05:30

      Дело в цене скорее всего. С точки зрения владельцев такой конторы можно пойти по пути Qt и искать программёров за $150к или выбрать JS и платить раза в 1.5 меньше. К тому же со временем процессоров в телефонах будет всё больше, батарейку есть они будут всё меньше, и весь этот JS будет работать в фоновом потоке (подозреваю, что оно уже так и работает).


      1. immaculate
        20.04.2017 07:37

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


        1. MikailBag
          25.04.2017 19:35
          -1

          И это ему не мешает.


    1. Nagg
      20.04.2017 20:14
      +2

      100 лет будете писать на Qt такой же rich гуй, хотя скорее всего плюните и полгуя будет в QtWebView на том же хтмл+жс.


      1. Crandel
        21.04.2017 15:40
        -2

        У ворда rich gui, а в слака только список чатов, что там сложного для реализации? Почти вся логика на сервере работает.
        Разве меня интересует, как они его будут делать? Пускай нанимают нормальных спецов, PoC у них уже давно прошел


  1. Aingis
    19.04.2017 18:10
    -2

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

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


    1. Nakosika
      19.04.2017 18:42
      +10

      Типизированный язык это новый вид проверки кода. Каждый раз когда вводится новая проверка кода находятся ошибки, это неизбежно. Абсолютно не зависимо от того как хорошо были поставлены код ревью или написаны тесты, новая проверка — новые баги.


      1. Aingis
        19.04.2017 20:00
        -3

        А, собственно, почему он оказался новым, да ещё настолько сильно? Правильно, потому что линтера нет. О чём я и написал.


        1. ankh1989
          20.04.2017 05:31
          +4

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


          1. Aingis
            21.04.2017 15:29
            -1

            У них там не просто «куча всего», а «неожиданно много». Это однозначно говорит, что культуры кода нет. А значит нет ни линтера, ни грамотного код-ревью, да и не могло всё это пройти качественное тестирование. Вывод однозначен.


        1. Ch4r1k
          20.04.2017 13:23

          Линтеры то есть, вы имеете ввиду что ими не пользуется команда разрабов?


  1. CodeViking
    19.04.2017 23:39
    +2

    Всё вам не нравится, всё вы критикуете. А между тем — каждый использует Slack. Как говорится критикуешь — предлагай. Не нравится стартап — делай свой более достойный аналог. Нашёл выход из ситуации — пиши на хабре. Да electron жрет много памяти, ещё сам slack подъедает, однако не думаю что программисту проблема затариться памятью. Я сервисом доволен. У меня 5 тимов онлайн, 16гб ОЗУ ни разу ещё не забил со всеми chrome+jetbrains+amarok+deluge+etc


    1. raveclassic
      20.04.2017 00:40

      Я с вами полностью согласен, но никогда вы не продадите все это добро нативщику, покуда там есть инструменты, позволяющие минимизировать чрезмерное и, увы, в большинстве случае неоправданное потребление ресурсов. Да, сокращаются издержки на сотрудников и поддержку, но «попробуй объясни докажи».


    1. Aingis
      20.04.2017 14:26

      Яндекс вон как раз выпустил аналог.


  1. GeraldIstar
    20.04.2017 09:02
    +2

    Используем на проекте TypeScript. В целом неплохо, но часто сталкиваемся с проблемой, что на нужную библиотеку нет тайпингов, либо они не полные/устаревшие. С Ramda из-за этого неудобно работать. Компилятор не может свести типы (или тайпинги корявые?). Приходится как-то подстраиваться.


    1. arvitaly
      20.04.2017 18:28
      +1

      Это не проблема TS точно, никто не заставляет использовать тайпинги вообще, any никуда не делся. А по хорошему, pull request разработчикам, чтобы добавили исправленный d.ts в пакет или в types