«А он еще не умер?»,- спрашивают нас про Dart на каждой фронтенд-конференции. «А как Google поддерживает язык?», «как вы нанимаете разработчиков в команду?», «а почему не TypeScript, если вам нужна типизация?»
Мы решили объединить наиболее частые вопросы и задать их в интервью Игорю Демьянову, менеджеру по разработке Wrike.
Поговорим с ним о том, почему Wrike, с 2 млн строчками кода за спиной больше двух лет назад не побоялся перейти с JavaScript на Dart, как проходила миграция, как рос продукт и увеличивалась команда разработчиков, как развивается язык сегодня, несмотря на разговоры о его стагнации или даже смерти.
Игорь, продукту уже больше 10 лет, а что было до того момента, как вы начали использовать Dart для разработки фронтенда Wrike?
До Dart мы писали на ExtJS 3 и частично на ExtJS 4. Плюс у нас был небольшой самодельный фреймворк, который написал один из бывших сотрудников. Этот «велосипед» нам сейчас скорее мешает, и мы от него потихоньку избавляемся. На данный момент в проекте более 2,5 млн строк клиентского кода, новый функционал мы уже года два пишем на дарте и потихоньку избавляемся от легаси на ExtJS.
В какой момент и почему пришло понимание, что надо переставать писать на JS?
Оно пришло, когда Wrike начал активно расти. Когда у нас в команде было 11 человек — мы еще как-то могли использовать JavaScript, более-менее понимать, кто какой код пишет, помнить все нюансы старого кода. Но сейчас в команде фронтенда у нас около 50 человек, разработчики распределены более чем по 10 scrum-командам, и правило личных договоренностей уже не работает. Задач много, разработчиков много, кода много.
Много кода — это сколько?
На клиент мы в данный момент грузим около 10 мегабайт сжатого кода, раньше было 5 или 6. И раньше эта цифра выглядела страшно, сейчас и подавно. Но мы тут ничего не можем сделать -продукт активно растет функционально. Разумеется, мы стремимся, чтобы отдельные куски кода грузились по требованию, но в каких-то случаях приходится подгружать весь код сразу, чтобы весь функционал Wrike был доступен клиенту мгновенно.
Так а почему Dart?
Dart выгодно отличается от всего остального тем, что он может выкидывать неиспользуемые части кода, это и позволяет нам жить с таким огромным легаси. Когда проект большой и разработчиков много, очень сложно контролировать, что ты используешь, а что нет. Есть код, который лежит в кодовой базе — может быть, его просто кто-то забыл выкинуть из общей сборки — и он все еще живет с нами. И если бы мы использовали JS с его сборщиками, этот код точно приходил бы на клиент. А Dart позволяет оптимизировать и выпилить ненужный код. Плюс мы не зависим ни от каких стандартов JS, которые постоянно меняются и обновляются. Кода ты делаешь enterprise-продукт, очень сложно выбить у бизнеса время на перевод с одного стандарта на другой.
Выбрав Dart, мы пошли по пути медленной миграции: вначале переписали корную часть, затем — остальной функционал. Сейчас у нас уже грузится нового кода больше, чем старого. При этом продукт активно развивается и новые фичи приходится делать очень быстро.
С точки зрения команды, процесс тоже поменялся — разработчики меньше времени тратят на разбирательства, какие параметры куда передаются.
Да, сейчас примерно эти же возможности есть у Typescript или JS+Flow, но нам нравится Dart. У языка простой и понятный синтаксис, а главное — он позволяет нам концентрироваться на идеях, экономить время, которое бы мы тратили на рефакторинг или, скажем, на переход с ES5 на ES6, фреймворки и т.п.
Сейчас ведется какая-то разработка компонентов продукта на JS?
В отдельных исключительных случаях мы можем вносить изменения в легаси-код на JS, если видим какой-то critical bug. В остальных случаях пишется новый код на Dart — он либо заменяет старый код, либо добавляет новый функционал.
Какой фреймворк вы используете?
Примерно год назад мы перешли с Polymer на Angular2. Сейчас планируем переход на Angular3, а к концу года — на Angular4. Во многих компаниях бизнес в этом отношении довольно тяжело идет навстречу R'n'D — такие миграции сопровождаются долгими спорами и убеждениями. То, что мы используем самые новые инструменты — не только заслуга фронтенд-разработчиков, но и наших тестировщиков-автоматизаторов, которые хорошо помогают нам с регрессионными тестами и QA Manual. Когда мы переходили на Dart, то получили поддержку всех технических команд Wrike, поэтому этот переход нам дался относительно безболезненно. Так же и с Angular — прогнозируем, что переход между версиями затянется на 1-2 месяца, вряд ли больше.
Сейчас мало знают о языке Dart, но 2 года назад информации же вообще не было. Как вообще вам удалось по живому в работающем проекте все переделать? Трудно представить, что бизнес на это вообще пошел.
Я сам начал использовать Dart с версии 0.8, когда он был еще только в бете. На момент внедрения в Wrike Dart уже был официально зарелижен, появилась спецификация, стандарты и т.п., то есть мы были уверены, что принципиально в языке ничего не будет меняться. Мы знали, с каким языком будем иметь дело, все проанализировали и получили первые результаты, которыми смогли аргументированно доказать необходимость изменений. Главным доводом в пользу дарта был Tree Shaking — умение дарта выпиливать неиспользуемый код вплоть до методов. А это при нашем объеме legacy критично.
Почему не TypeScript?
Если бы мы, допустим, портировали Wrike на TypeScript, это было бы, наверное, «дешевле». С другой стороны, у нас столько кода, что портирование на TS, нам скорее всего ничего бы не дало — остался бы тот же объем подгружаемого легаси, только на TypeScript. Dart же заставляет нас писать лаконично и правильно, и не дает допустить простых ошибок в проектировании, это его плюс, но в этом, конечно, и его строгость.
Если вернуться к бизнесу, то да, здесь мы получили поддержку. Наш CEO Андрей Филев — сам в прошлом разработчик, и разговор с ним можно вести на одном языке. Плюс, репутация Google как разработчика языка о многом говорит. В конце концов, эти изменения были необходимы и самому бизнесу, чтобы максимально быстро разрабатывать и выпускать фичи, позволяющие нам оставаться одними из лидеров рынка. В общем, бизнес поддержал, и сейчас мы очень быстро движемся, а бизнес доволен нашей скоростью.
Но вот те 11 человек, которые писали на JS и вынуждены были переходить на Dart, наверно, не так радостно восприняли эту новость?
Ребята, которые поработали с каким-то другим языком, кроме JavaScript, очень легко переходили на Dart. Они, наоборот, были довольны, что дарт решает их проблемы с чужим кодом и упрощает коммуникацию. Конечно, были и те, кто знал только лишь один JavaScript, и, как и любой «старовер», в штыки воспринимал изменения.
Сейчас в JavaScript-мир довольно низкий порог вхождения, очень много в сообществе новичков, которые осваивают язык, но едва ли понимают, как проектируются большие продукты. У моего коллеги Евгения Гусева есть доклад на эту тему, который не так давно вызвал немалую полемику среди JS-разработчиков.
Любой язык можно рассматривать только в контексте его применения, поэтому тут споры и холивары бессмысленны. Например, JavaScript всем хорош для прототипирования — как бы вы ни написали код на JS, вы его скорее всего запустите. Нет понятия “плохой JavaScript” — либо он работает, как вам нужно, либо он работает не так, как вам нужно, и все.
С Dart сложнее. У вас, как в Java и как в .net, есть анализатор, который вам говорит: «Знаешь, дружок, как бы не так. Так нельзя делать. Нельзя создавать динамические классы и т.п.». То есть язык просто не позволит самому себе выстрелить в ногу, как бы вы ни хотели.
Если вы посмотрите, многие создатели Dart, работали с серьезными объекто-ориентированными языкам, V8 делали, даже кто-то из Java у них есть. Они подошли к проектированию языка с точки зрения больших приложений, поэтому Dart – это язык для средних, больших, и очень больших приложений. Он по дефолту предлагает одно, но самое эффективное решение, и оно идет «из коробки». Вы в один клик можете сделать отложенную загрузку/загрузку по требованию, язык сам будет «выпиливать», оптимизировать код – это те бонусы, которые нужны большим приложениям. На маленьких приложениях вы практически никакого заметного эффекта по сравнению с JS не увидите.
О том, как развивается язык, что нового в нем ждать в ближайший год, насколько охотно разработчики языка из Google участвуют в жизни сообщества и решают возникающие проблемы, читайте во второй части интервью примерно через неделю.
Мы будем благодарны за вопросы в комментариях и, если потребуется, напишем более подробные технические статьи о работе с Dart на их основе.
Комментарии (49)
SonicGD
15.06.2017 13:09-3> Сейчас планируем переход на Angular3, а к концу года — на Angular4
Angular 3?AndreiChernykh
15.06.2017 13:16+1См. ответ выше. В Dart версии Angular своя кодовая база и своё версионирование, не привязанное к Angular на TypeSctipt.
justboris
16.06.2017 11:20Для Dart есть свой Angular, вот тут: https://github.com/dart-lang/angular2
И у него последняя версия действительно 3.
qtask
15.06.2017 17:34+1Polymer сейчас тоже активно развивается, почему решили перейти на Angular2? В вашей компании есть опыт совместного использования обоих инструментов?
bunopus
16.06.2017 12:18Мы юзали Polymer (тогда ещё 0.5 версии). После выхода версии 1.0 использовать его стало невозможно. В целях «оптимизации» оттуда выкинули очень много всего. В итоге перешли на Angular,
Последнюю версию Polymer ещё не пробовали, но сильно сомневаюсь, что он побъёт Angular по богатству фичей, всё таки это скорее библиотека компонентов + шаблонизатор
janitor
16.06.2017 20:17Потому что решили использовать Polymer, т.к. «стильно, модно, от Google», а потом оказалось, что он тормозной, более-мене работает в Chrome, а в стабильной версии (1.x) и убрали многие фичи к тому же.
degorov
15.06.2017 18:55+1А вот у Rollup есть tree shaking с ES6 модулями, это чем-то принципиально отличается от того, что в Dart?
kana-desu
15.06.2017 22:41+1На всякий случай добавлю, что у webpack тоже есть tree shaking с ES6 модулями (если используется babel, нужно в конфиге отключить трансформацию импортов/экспортов), да и google closure compiller тоже вроде как умел в такое.
bunopus
16.06.2017 12:32Тут есть принципиальное различие: Tree shaking на уровне модулей это типа
// a.js class A { someMethod() { //... } } class SomeVeryBigClass { //... } export { A, SomeVeryBigClass } -------------------------- // b.js export class B { anotherMethod() { //... } } ------------------------- // c.js import 'a.js' let a = new A();
В случае "модульного" перетряхивания модуль b.js не будет подключен. Однако все классы из модуля a.js будут присутствовать, внезависимости от того, используются ли они или нет.
Что же Dart?
// a.dart class A { someMethod() { //... } } class SomeVeryBigClass { //... } ------------------------- import 'a.dart' main() { var a = new A() }
В случае Dart перетряхивание уберёт не только класс SomeVeryBigClass, но и someMethod из класса A, так как он не используется.
Итого разница — Tree Shaking в Дарт более гранулярный, он может убирать всё, вплоть до методов и свойств класса.degorov
16.06.2017 12:42Rollup и Webpack2 убирают dead code, я, собственно, потому и спросил.
"entities that are neither exported nor used inside their modules do not appear in the minified bundle"
bunopus
18.06.2017 17:02Нашёл специально хорошую статью про Rollup и его ограничения
https://medium.com/@Rich_Harris/tree-shaking-versus-dead-code-elimination-d3765df85c80
degorov
16.06.2017 12:46А фраза «с ES6 модулями» касалась того, что с CommonJS он так же делать не умеет.
janitor
16.06.2017 20:21Ну а есть какая-то статистика по проекте, сколько в итоге этот Tree Shaking кода выпиливает? Из всяких Ангуляров и прочих зависимостей, и собственного кода?
bunopus
20.06.2017 12:21К сожалению самой свежей инфы нет. Сейчас в планах перейти на Dart 1.24, он делает более эффективную компиляцию в JS. Тогда и обновим данные
impwx
16.06.2017 10:53+2Dart заставляет нас писать лаконично и правильно
А Typescript заставляет писать объемно и криво, подталкивает к ошибкам при проектировании?..
Я прочитал параграф про «Почему не Typescript» два раза, но ответа на вопрос так и не нашел. Судя по всему, объективной причины нет и просто «так сложилось».justboris
16.06.2017 11:27Typescript все-таки является расширением Javascript. Грубо говоря, можно переименовать файл js -> ts, и код все равно будет собираться.
Dart это совершенно другой язык, со своей парадигмой. Писать в Javascript-стиле на нем не получится. Поэтому можно сказать, что на Dart код получится "правильнее".
impwx
16.06.2017 11:52+1Грубо говоря, можно переименовать файл js -> ts, и код все равно будет собираться.
Если задача — не дать разработчикам схалявить, то можно включить жесткие проверки компилятора —noImplicitAny, noUnusedLocals, allowUnreachableCode, allowJs
.
Поэтому можно сказать, что на Dart код получится «правильнее».
Нельзя. Парадигма другая, но это не делает ее априори «правильной».justboris
16.06.2017 12:21Именно поэтому я взял слово "правильно" в кавычки. Имеется в виду, что на Dart получится более Javaподобный enterprise код чем на Typescript. Во Wrike захотели именно такого, но это не значит что это нужно всем.
Кроме того Dart это совершенно отдельный язык, такой же как и Kotlin, что может транслироваться в JS, а Typescript — это расширение JS.
impwx
16.06.2017 13:11+1на Dart получится более Javaподобный enterprise код чем на Typescript
А можно конкретный пример? Я не знаток Dart, но в списке возможностей на сайте не нашел ничего такого, чего нельзя было бы реализовать в Typescript (кроме, наверное, перегрузки операторов).
Кроме того Dart это совершенно отдельный язык, ..., а Typescript — это расширение JS.
Каким образом два этих факта говорят в пользу Dart?justboris
16.06.2017 14:39Я слежу за большим проектом на Dart: https://github.com/sass/dart-sass
Код там больше напоминает Java или C#, где в основном используются классы и ООП.
На Typescript в таком стиле тоже можно писать. Но сам язык этого не требует, можно писать просто функции. Вот пример из исходников самого Typescript:
https://github.com/Microsoft/TypeScript/blob/master/src/compiler/core.ts
Поэтому те, кто хотят получить себе ООП в основе языка, а не в виде сахара поверх прототипов, выбирают Dart.
bunopus ниже показывает то же самое на примере типов Car и Ford.impwx
16.06.2017 14:55Но сам язык этого не требует
Судя по исходникам указанного вами же проекта, Dart тоже не требует. Да и в целом, язык должен быть гибким и позволять организовать архитектуру как удобно, а жесткие ограничения — это задача фреймворков и корпоративного стайлгайда.
bunopus
16.06.2017 12:57Ну если вкратце:
Типизация Dart более "строгая". Каноничный пример TS
interface Car { drive(); } interface Plane { drive(); } abstract class Ford { } class Focus extends Ford implements Car { drive() { console.log('I am driving'); } } let my_car: Focus = new Focus(); console.log(my_car instanceof Ford); // true console.log(my_car instanceof Car); // Нельзя проверять интерфейсы в runtime!!!
окей, пишем guard, но только на Plane
function isPlane(arg: any): arg is Plane { return arg.drive !== undefined; } console.log(isPlane(my_car)); // true, несмотря на то, что my_car другого интерфейса
Да, можно сказать, что интерфейс это просто набор методов и свойств, так что всё правомерно, но это не так.
В Dart есть такая штука https://www.dartlang.org/guides/language/sound-dart
В Dart есть SDK. Туда входит то, для чего в TS и JS надо подключать сторонние библиотеки. Которые имеют свойство устаревать, конфликтовать и пр. Это напрример потоки (rx.js), зоны (zone.js) и многое другое
impwx
16.06.2017 14:01Вы пытаетесь скормить Typescript код, написанный для языков вроде Java / C# / C++, без учета местных идиом и особенностей. Неудивительно, что он работает не так, как вы ожидаете.
То, что в Typescript описывается ключевым словомinterface
, на самом деле является формой класса. Она действительно описывает только список методов и свойств и существует только на этапе компиляции. Поскольку типизация с Typescript структурная, классу не обязательно явно указывать интерфейс при объявлении, чтобы его реализовать — даже анонимный класс с необходимыми полями подойдет! С таким подходом можно писать более гибкие абстракции, не лишаясь строгой типизации — например, когда вы используете чужие классы и не можете внести в их объявление собственный интерфейс.
По поводу type guard: а почему вы решили, что на основании наличия методаdrive
можно сделать вывод о том, что объект принадлежит типуPlane
? С точки зрения структурной типизации два интерфейса с одинаковыми сигнатурами являются одной и той же формой. Если нужно различать произвольные объекты, то можно использовать tagged union types.justboris
16.06.2017 14:42Вы пытаетесь скормить Typescript код, написанный для языков вроде Java / C# / C++, без учета местных идиом и особенностей. Неудивительно, что он работает не так, как вы ожидаете.
Немного не так. Не все хотят разбираться с особенностями местных идиом и особенностей, а просто хотят писать так же, как привыкли в Java или C#.
Dart эту возможность предоставляет.
bunopus
16.06.2017 14:54Я напомню, изначально я отвечал на вопрос
Я прочитал параграф про «Почему не Typescript» два раза, но ответа на вопрос так и не нашел. Судя по всему, объективной причины нет и просто «так сложилось».
Вот вам ответ: Dart мне позволяет писать в полностью ООП стиле. Именно в том, который в Java / C# / C++.
Местные идиомы и особенности наверное хороши для тех, кто пришёл из JS и не имеет опыта в других языках, я же не осуждаю.impwx
16.06.2017 15:05Вот мы наконец-то и докопались до истины. Более низкий порог вхождения с учетом существующего опыта команды — это весомый аргумент.
С другой стороны, при работе с новым языком всегда приходится изучить определенный объем тонкостей — синтаксис, best practices, стандартная библиотека. На фоне этого объема различия между прототипным и классическим наследованием не выглядят такими уж существенными.
vintage
22.06.2017 06:53Да нет, каноничный вариант на TS будет выглядеть так:
abstract class Car { readonly brand : string abstract drive() : void } abstract class Plane { readonly brand : string abstract drive() : void } class Focus extends Car { readonly brand = 'Ford' drive() { console.log( 'I am driving' ) } } const my_car = new Focus console.log( my_car.brand === 'Ford' ) // true console.log( my_car instanceof Car ) // true
Это например потоки (rx.js), зоны (zone.js)
И зачем эти антипаттерны в стандартной библиотеке? :-)
bunopus
16.06.2017 13:02Порождаемый Dart код может быть быстрее, чем js. Да, тут наверное самый скользкий плюс, так как он сильно разнится от примера к примеру. Но несомненный плюс в том, что в Dart действует парадигма из Java "Write once — Compile Everywhere". Т.е. когда я пишу код на Dart — я знаю, что с выходом новой версии v8 например, он будет скомпилирован в максимально эффективный js код. Например с выходом DDC (Dart dev Compiler) код компилируется в ES6.
janitor
16.06.2017 20:25Вкратце — потому что не от Google. Других причин нет, просто 1-2 людям, кто принимал решение и продвигал Dart, очень нравились (нравятся?) продукты от Google (SoyTemplates, Google Closure Compiler, Polymer, Angular, Dart)
DronVelikii
16.06.2017 13:40Старые куски кода на js используете через package:js? Проблем с отложенной загрузкой не возникало?
bunopus
16.06.2017 15:43Да, через него. Ну, каких-то особых не замечено, есть какие-то конкретные примеры?
DronVelikii
16.06.2017 17:13Не нашел в дарте инструмента для подгрузки (deferred loading) js скриптов. Можно подгрузить классы-обертки над js кодом, но это не то, сам js скрипт не подгрузится таким способом.
И получается все js скрипты надо гвоздями приколачивать или самому загрузчик писать
eugenk
19.06.2017 10:40-1Насчет выпиливания честно говоря немного удивлён. Сам сейчас пишу небольшой учебно-тестовый проект на dart (это просто новый мастер установки некоего моего ранее написанного продукта), до того писал на typescript. Резко не понравилось(по сравнению с typescript) то, что проект в тысячу строк порождает 300 килобайт компилированного кода на javascript (до минимизации). Сейчас с большим интересом смотрю в сторону scala.js. Очень жалею что не узнал об этой технологии раньше. И по-моему если уж говорить о чём-то заменяющем javascript, смотреть следует именно в эту сторону, а не на typescript, dart, kotlin и т.п. Дело в том, что возможно всё это и очень неплохие языки, но увы, все они нишевые. Нет, если разработчик желает постоянно оставаться в этой нише то пожалуйста. Но если он хочет быть более универсальным, то и изучать следует универсальный, пригодный почти для всего язык. Такой как scala.
bunopus
20.06.2017 12:28-2Эм. Ну вы уж простите, но как по мне scala ещё более "нишевый" язык. И насчёт
универсальный, пригодный почти для всего язык. Такой как scala.
Звучит очень оптимистично
https://trends.google.com/trends/explore?q=scala.js,%2Fm%2F0h52xr1eugenk
20.06.2017 19:46+1Ваша ссылка реально ни о чем. Ибо показывает не достоинство того или иного языка, а всего лишь его популярность. Ну а к утверждению что scala язык ещё более нишевой вообще не знаю как относиться. Что я могу владея scala:
1) Писать для десктопа под JVM
2) Писать для сервера (play 2, lift, etc)
3) Писать клиентский код для броузера (scala.js)
4) Заниматься data science (apache spark, tensorwlow, куча явовских библиотек)
5) Заниматься хардварным дизайном https://chisel.eecs.berkeley.edu/ (на этом кстати написан новый опенсорсный процессор RISC-V)
Единственно для чего scala непригодна, это для мобильной разработки(слишком толстый исполняемый код) и написания нативного кода. Да и то мобильная разработка станет вполне возможной в ветке scala-3, в которой обещают оптимизатор, а работа над нативным кодом идёт полным ходом (scala-native). Не покрытыми начиная с ветки 3 остаются пожалуй только две области. Это .NET и встроенные системы. Но к .NET возможно ещё вернутся. А для встроенных систем (работал с ними очень много) я даже С++ применяю с большой осторожностью и пишу фактически на С с классами. Ну и скажите на милость, что из всего этого умеет dart? Мне кажется беда scala — его репутация сложного языка. Причем совершенно не заслуженная. Его изначально боятся. Поэтому и Ваша ссылка показывает то что показывает.bunopus
20.06.2017 23:31+1А мы сейчас обсуждаем, что лучше Dart или scala? Я не знаю, так как на скале не писал. Из того, что вы перечислили Dart умеет всё, ну кроме маргинальных "data science и Заниматься хардварным дизайном"
Инструмент должен уметь делать что-то хорошо, а не всё, иначе он превратится неизвестно во что.eugenk
21.06.2017 01:18Да нет, что Вы! Избавь боже от обсуждений какой язык лучше, я давно уже вырос из того возраста когда это обсуждают. Я просто рассуждаю с сугубо потребительской точки зрения. Есть две технологии, А и B. Обе решают некую нужную мне здесь и сейчас задачу. Насколько хорошо и удобно они её решают, я пока не знаю, поскольку не владею ни А ни B. Однако мне известно, что область применимости B шире чем A. Спрашивается, какую из двух технологий я выберу для изучения и освоения? Наверно всё-таки B если она конечно не совсем уродская. Это единственное что я хотел сказать.
И кстати ни data sciense ни хардварный дизайн мне маргинальными не кажутся. Вторым я очень много в своё время занимался, правда на традиционном верилоге. Первым очень интересуюсь сейчас. На scala для продакшена пока ничего не писал. Сейчас этот язык у меня в стадии активного освоения. Следующий веб-проект хочу с нуля делать именно на scala.js. Кстати будет очень хороший повод сравнить scala и dart по удобству. Возможно даже напишу об этом на хабре.
xakboard
Angular3. Когда еще не знаешь что Angular 4
AndreiChernykh
Версионность в Dart версии и в TypeScript версии отличается.
В TypeScript они просто третью версию перескочили.
xakboard
Тогда не Angular a AngularDart
Wriketeam
Так в статье речь и идет о Dart
bunopus
Это назвается К — Консистентность. Команда AngularDart решила не перепрыгивать через версию, в отличие от TS команды