«При роботі над інспекціями коду зустрічаєш Java-паззлеры кожен день»: Тагір Валєєв про роботу над IntelliJ IDEA



Одні знають Тагіра lany Валєєва за його доповідей про Stream API, інші — за хабрапостам, треті — по роботі над статичним аналізом коду (у проектах FindBugs і HuntBugs). А з серпня він працює в JetBrains над IntelliJ IDEA, і це на багато вплинуло: аналізом коду він тепер займається саме там, та його наступний доповідь (на наближення JPoint і JBreak) буде як раз про створення інспекцій коду в IDEA. Ми розпитали Тагіра про те, як розробляти проект, в якому розробляють все інше.

— Як ти почав працювати над IDEA? Сам був активним користувачем і хотів розвивати інструмент, або причини були зовсім іншими, і до цього сидів взагалі в Eclipse/NetBeans?

— До роботи в JetBrains я IDEA майже не користувався. В основному сидів в Eclipse. Іноді доводилося працювати в NetBeans, тому що ми писали RCP-додаток на ньому.

Я хотів працювати над аналізом Java-коду, а для цього IntelliJ IDEA — ідеальне місце. Раніше я рухав FindBugs, потім почав писати свій статичний аналізатор байткода для Java HuntBugs, але все-таки зрозуміло, що в IDEA величезний пласт роботи вже зроблено, а головне — не доведеться тягнути самому. Приємно робити щось унікальне, а не наздоганяти інших. Ну і, звичайно, аналізатор коду, спочатку інтегрований з IDE, набагато більш корисний, тому що часто можна зробити quick-fix, який в один клік виправляє проблему в коді. Переглядати висновок FindBugs (навіть якщо він інтегрований з IDE) і виправляти вручну істотно більше. В результаті люди перестають взагалі піклуватися про статичному аналізі, навіть якщо він у них виконується на build-сервері.

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

— Так, згоден, тут є якийсь провал. Аналіз коду в IDEA можна запустити з командного рядка, не піднімаючи UI, крім того, вона інтегрується з TeamCity і UpSource. Але, наскільки мені відомо, інтеграції з Jenkins або тупо maven-плагіна немає. Можна було б зробити хоча б для IDEA Community Edition, де не постає питання перевірки ліцензії, але, по-моєму, зараз для компанії це не є пріоритетом. Більше того, інтеграція з IDEA — це selling point TeamCity і UpSource. З іншого боку, це виглядає нескладним завданням. В принципі, будь-ентузіаст міг би подружити IDEA Community Edition і Jenkins, багато хто б від цього тільки виграли. Було б зручно, якби на будь-пулл-реквест або отримати малювалася табличка в Jenkins, де б показувалося нові і виправлені проблеми в коді.

— Працювати над IDEA — це такий метарівень, «розробляти головний інструмент розробки». І тому у людини «зсередини» цікаво запитати: а чим на практиці така розробка відрізняється від звичайної?

— Так, тут є цікаві особливості. Звичайно, і в інших випадках буває, що розробник сам користується проектом, який розробляє. Але при розробці IDEA це доведена до максимуму. Зазвичай я оновлюю основну інсталяцію IDEA кожен день або через день (це спрощує JetBrains Toolbox) з нічного збирання і працюю з самим свіжим кодом, який сам вчора написав. Деякі розробники оновлюють інсталяцію спеціальним скриптом прямо з поточного коду, выкачанного локально. Це допомагає відловлювати багато баги до того, як їх побачать зовнішні користувачі, або зрозуміти, що нова можливість не дуже зручна. Ну і в цілому приємно користуватися тим, що сам зробив. Наприклад, я писав кілька поліпшень для плагіна DevKit, який допомагає розробляти саму IDEA або плагіни до неї. На наступний день самому стає зручніше працювати.

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

— Існуючий код намагаємося не чіпати масово без необхідності, щоб не ускладнювати історію git і не зламати те, що і так працює. Мені особисто деякі речі хотілося б застосувати глобально. Скажімо, цикл з видаленням елементів з колекції за умовою досить непогано замінюється моєї інспекцією на Collections.removeIf, який не тільки коротше і зрозуміліше, але і, наприклад, для великого ArrayList працює значно швидше, ніж видалення ітератором. Ітератор буде при видаленні кожного елемента зрушувати внутрішній масив, а removeIf зрушить всі один раз. Якщо я починаю чіпати код, який давно ніхто не чіпав, я, звичайно, намагаюся його спростити, та інспекції мені в цьому допомагають.

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

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

— У випадку з інспекціями контринтуитивные запити від користувачів нам особливо не заважають: можна зробити дві інспекції, одна замінює A на B, а інша замінює B на A, і нехай користувачі вирішують, що їм подобається. Багато хто дійсно не люблять або не розуміють стріми і їм приємніше розгорнути стрім, написаний колегою-хіпстером, у цикл. Але є і об'єктивні недоліки у стримов: деяких операцій просто немає, важче налагоджувати, важко кинути перевіряється виключення або модифікувати локальну змінну. Скажімо, написали ви стрім і тут зрозуміли, що вам потрібен достроковий вихід за умовою. Операція takeWhile з'явиться тільки в Java 9, сторонні бібліотеки зразок StreamEx бос використовувати не дозволяє. А тут в два кліка можна стрім перетворити в цикл і дописати потрібний break. Загалом, я глибоко переконаний, що це корисна фіча.



— В недавньому випуску «Без слайдів» Максим Шафіров визнавав, що догфудинг при своїй користь для JetBrains все ж обмежений: одні проблеми відчуваєш на власній шкірі дуже добре, але на інші особисто не натикаєшся. Чи Часто ти зустрічаєшся з ситуаціями, коли люди використовують IDEA зовсім не так, як ти, а в результаті їм потрібно таке, про що ти й не здогадався б? Наскільки тобі допомагає досвід попередньої роботи, де ти сам бачив IDE не з того ж ракурсу, що зараз?

— Я часто бачу, що люди пишуть виключно дивний код. Пишуть три виклику методів там, де можна написати один. Пишуть десять рядків, знімаючи зайву копію з колекції, там, де можна написати три рядки і не копіювати. Це видно по запитам в YouTrack (ось, наприклад, нещодавно закривав такий), за кодом, який я бачу на GitHub, і навіть за кодом IDEA, який пишуть інші розробники. Мабуть, багато програмісти вивчили кілька API-методів або класів і намагаються всі завдання звести до них. В цьому плані дуже корисно читати чужий код, тому що мені б і в голову не прийшло написати так дивно. Бачиш очевидну дурість в чужому коді, яку IDEA не підсвічує — виникає думка написати нову інспекцію.

Мені в певному сенсі допомагає досвід роботи в Eclipse. В цілому у нього Java-tooling помітно гірше, ніж у IDEA, але це не означає, що він гірший за всіх фронтах. Деяких штук після Eclipse мені дуже не вистачає і, можливо, дещо-що я зроблю, без чого страждаю.

— Ти активно спілкуєшся з іншими розробниками (сидиш в чатике «Розбору польотів», твитишь, пишеш хабрапосты), та часто стає темою обговорень як раз IDEA. Таке спілкування допомагає в роботі над нею, або це для душі?

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

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

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

— Коли виходить нова версія IDEA, люди бурхливо обговорюють впадають в очі речі: підказки з назвами параметрів, недавню зміну іконок і так далі. А для тебе це виглядає зсередини: «всі обговорюють вершину айсберга, не помічаючи головного», або це дійсно і є основне з того, що відбувається?

— Важко сказати, яке зміна головніший. Яке найбільше прискорило роботу користувача? На що ми витратили найбільше часу? Яке викликало самі захоплені відгуки? Звичайно, робиться багато речей, яких ніхто не бачить. Наприклад, постійно триває робота над розвантаженням UI-потоку (Event Dispatch Thread) і поділом логіки і відображення. Ще в 2016.3 можна було взяти блокування на запис файлу в UI-потоці, а потім, не знімаючи її, вивести UI-діалог. Взяття блокування може розуміти очікування, поки файл відпустить інший потік. Зараз з цим поборолися: write-lock'і в UI-потоці брати заборонено. Зрозуміло, довелося перелопатити багато коду, який намагався виводити UI під час блокування: удаляли зайві write-lock'і, де-то выносили UI-код з них. На це пішло багато годин, але користувач помітить лише невелике збільшення чутливості IDE. Що тут бурхливо обговорювати?

— до Речі, цікаво запитати про зміну іконок: а у тебе самого від неї якісь відчуття?

— Я, якщо чесно, нічого не помітив, тому що я не був користувачем IDEA. Нові іконки з'явилися у внутрішніх білдах до мого приходу в компанію, або це відбулося протягом перших тижнів, за які я не встиг звикнути до старих.

Чесно сказати, я не знаю, чому люди приділяють так багато уваги іконок. Під час роботи в IDE я дивлюся на код, а не на іконки. Щоб відкрити клас або файл, я не шарюсь в дереві проекту, а користуюся гарячими клавішами начебто Ctrl+E, Ctrl+N Ctrl+Shift+N. У дерево з іконками я іноді дивлюся, коли створюю нову директорію або новий файл (хоча часто простіше скопіювати існуючий файл через F5, ввести нове ім'я і видалити вміст). Тулбар у мене приховано, в меню я теж зазвичай не лізу, воліючи Ctrl+Shift+A enter дії текстом (або конкретну гарячу клавішу). Я користуюся IDEA недавно і, ймовірно, ще не оптимально. Думаю, з часом я буду бачити іконки ще рідше. Нехай вони будуть хоч в стилі Windows 3.1, мені особливої різниці немає.



— До питання про те, що JetBrains активно користуються своїми власними продуктами: а ось які при роботі над IDEA використовуються продукти не від JetBrains?

— Ну git, наприклад. Використовується Confluence, наприклад, для публікації EAP'ів для деяких внутрішніх цілей. Java не від JetBrains. Профилировщики які-небудь. Ну, веб-браузер…

— За 16 років проекти неминуче обростають легасі-проблемами, що заважають рухатися вперед. Які у вас вживаються заходи для боротьби з цим?

— У коді IDEA не все так погано. Набагато більше добре, ніж погано. Є очевидні проблеми (наприклад, API для плагінів). Дещо нелогічно розкидане по проекту. Але це все не поростає мохом, а регулярно освіжається. Заходи звичайні: порефакторить тут, порефакторить там.

— У продовження попереднього питання: а як вам живеться з Swing-інтерфейсом 2017-го, коли Atom і Visual Studio Code роблять взагалі на Electron? Це заважає чи ні?

— Я майже не займаюся UI і навряд чи знаю про серйозні складнощі у ньому. Знаю, що багато проблем Swing (наприклад, відсутність підтримки HiDPI) JetBrains активно виправляє в своєму форке JDK. У мене є певне упередження з приводу UI HTML/JS, хоч і всі зараз на ньому. Там занадто багато свободи, що, занадто мало порядку. Пам'ятаю, в кулуарах Joker 2015 хлопці з JetBrains обговорювали думки переписати UI JavaScript, а Микита Липський їх відмовляв. Якщо я правильно розумію, зараз така розмова вже активно не ведеться — значить, в принципі з Swing поки можна жити.

— Зараз все частіше чути про Language Server Protocol — а ви цього не бачите сенсу брати участь, або все ще може змінитися?

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

— Відомо, яку велику роль грає Stack Overflow в ролі звичайного розробника. А з такими завданнями, як у тебе, він виявляється корисний чи ні?

— Я рідко в роботі знаходжу відповіді на Stack Overflow. Зазвичай на питання, в яких я зовсім новачок. Скажімо, знадобиться мені поправити gradle-скрипт. Я їх ніколи не писав, але тисячі людей писали, тому тут Stack Overflow мені напевно в чомусь допоможе. А якщо я в чомусь вже непогано розбираюся, у мене зазвичай такі питання, на які простіше з'ясувати відповідь іншими способами. Наприклад, заглянувши в исходники або документацію: якщо предмет для мене не новий, я швидше за все вже приблизно уявляю, куди дивитися.

— На JPoint і JBreak ти виступиш доповіддю про Java-інспекціях в IntelliJ IDEA — а можеш вже зараз навести якийсь приклад того, про що збираєшся розповісти там?

— Наприклад, ви пишете інспекцію, яка пропонує використовувати Map.getOrDefault, де це можливо. Скажімо, у вас є такий код:

public get Number(Map<String, Number> map) {
return map.containsKey("foo") ? map.get("foo") : 0.0;
}


Інспекція запропонує його замінити на такий:

public get Number(Map<String, Number> map) {
return map.getOrDefault("foo", 0.0);
}

Тут все нормально, результуючий код правильний і семантично еквівалентний вихідному. Отже, ви знайшли в исходнике тернарний оператор, з'ясували, що умова — це map.containsKey, map має тип java.util.Map, в істинній гілці map.get, квалификатор у map.containsKey і map.get — одна і та ж змінна, аргумент map.containsKey і map.get — одне і те ж «чисте вираження», а в неправдивої гілці константа. Тоді ми підкреслюємо код і пропонуємо замінити його на map.getOrDefault, де першим аргументом аргумент map.get, а другим — константа з помилкової гілки. Що ми не врахували? Де Java може нам підкласти свиню? На якому коді така інспекція зламається? Ось подібні питання ми і будемо розбирати в моїй доповіді.

— На JPoint 2016 ти з Барухом Садогурским і Євгеном Борисовим представляв Java-паззлеры (і на JPoint 2017, можливо, буде продовження). Багато хто сприймають паззлеры як «це цікаві курйози, але на моїй роботі вони рідко позначаються» — а на твоєму, виходить, що позначаються часто і сильно?

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



Якщо ви не бачили минулорічні виступи Тагіра на московській JPoint і новосибірської JBreak, рекомендуємо — глядачі високо оцінили їх:

Наступні JPoint і JBreak, де Тагір виступить з доповіддю про інспекціях, пройдуть у квітні — а вже з 1 лютого квитки на них подорожчають. Так що, якщо після інтерв'ю захотілося побачити цілком його виступ і інші доповіді цих конференцій, зараз саме час діяти.
Джерело: Хабрахабр

0 коментарів

Тільки зареєстровані та авторизовані користувачі можуть залишати коментарі.