Всем привет! С вами подкаст «Подлодка». Его ведущие Егор Толстой, это я, и Катя Петрова. Всем привет! Мы записали... Я понятия не имею, какой номер этого выпуска. Наверное, что-то типа 390 что-то там. И вот за эти четыре сотни невероятных выпусков мы успели поговорить про... API, про дизайн, про разные библиотеки. Но знаешь, Катя, о чем мы не говорили? Мы не говорили.
Да, не надо отвечать на мои риторические вопросы. Ты должна была этому научиться за 400 выпусков. Мы не говорили про API дизай... Нет, про дизайн API библиотек я запутался в трех словах, боже мой. И, конечно же, поговорить про эту тему мы позвали человека, ответственного за самую главную функцию в стандартной библиотеке Котлина – First Not Null of Or Null. Очевидно, что у такого человека накопило достаточно много экспертиз.
в том, как дизайнить хороший, понятный, человекочитаемый API. Позвольте мне представить вам Севу Толстопятого, главного за библиотеки в команде Kotlin. Привет-привет. Сев, перед тем, как ты расскажешь о себе, вот давай... Немножечко история. Все-таки вот first not null over null — это твоя рука или рука кого-то еще в команде? Это совместное творение меня, Ильи Горбанова и Ромы Елизарова. К нам однажды пришел пользователь и сказал, я хочу вот такую функцию, вот сделать вот такой вот юзка.
И у вас нет удобной функции в стандартной библиотеке для этого. Есть некоторый набор функций, которые делают то, что мне нужно, но по пути делают какую-то дополнительную ненужную работу. Мы недолго подумали, прикинули, что у нас есть на все консистентные названия, консистентный нейминг-паттерн, склеили все эти названия, и получился FirstNutNullForNull. Это очень контроверсная функция, потому что, с одной стороны, это частый объект шуток.
в том числе и внутри нашей команды. А с другой, люди в среднем, которые программируют на Kotlin, делятся на... Две группы большие. Одна, кто смеется над этой функцией, а другая, кто ее уже воспользовался и думает, что это самая лучшая функция в Kotlin. Код его чаще читают, чем пишут, и по этой функции идеально понятно, что она делает. Если вы программировали на Kotlin.
Как отличить джависта от человека из других экосистем? Вот, короче, вот примерно по длинным, хорошо читаемым именам. Попрошу, здесь нет слова factory в названии функции. То ли еще будет, то ли еще будет. Сев, давай, расскажи немного вообще про себя, про то, как так получилось, что ты стал, не знаю, не на фабрике, не на той самой фэкторе работать, а дизайнить API-библиотеку. Как тебя сюда...
занесло. Да, я, честно говоря, случайно здесь оказался. Ходил по разным метапам, в том числе ходил на метап Андрея Бреслова, который рассказывал про Котлин, задавал много неудобных вопросов. Мне сказали, ну, приходи, давай пособеседуемся, давай посмотрим. Неинтересно ли будет тебе работать в Котлине? Вот я пришел, и с тех пор так и не могу остановиться. Соответственно, я пришел работать над крутинами.
KotlinxKroutines — это фреймворк для синхронного программирования, библиотека для синхронного программирования на Kotlin. И очень быстро оказалось, что написать код-то несложно. И в целом имплементация — это не самая сложная часть любой функциональности. А сложно предоставить такую функциональность, чтобы всем было удобно, приятно, понятно, легко и просто ей пользоваться, а ошибаться было нельзя. Собственно, мы выработали какие-то...
набор принципов, набор дизайн-процессов, наши подходы, собственно, к разработке этого API. И так получилось, что это отлично экстраполировалось на соседней библиотеке, на стандартную библиотеку, где-то это пересекалось с принципами языка. И так и повелось. что после этого я занимаюсь примерно всеми библиотеками стандартными и полустандартными, которые есть в Котлине, и даже какими-то вещами вокруг. Окей, спасибо за то, что рассказал.
Партнер сегодняшнего выпуска — Контур. Я отлично помню, когда впервые столкнулся с их продуктами. Это был, наверное, 2017 год. Я тогда впервые зарегистрировал ИП и находился в экзистенциальном ужасе от того, что скоро придется разбираться со всеми страшными отчетностями, заполнять какие-то непонятные бумажки, высчитывать налоги до копейки и все время жить в страхе, что ты... недоплатил и стал налоговым преступником.
Так вот, тогда кто-то мне посоветовал попробовать сервис Contour Elba, который забирал на себя все бухгалтерские вопросы. И мне прям дико понравилось. Причем и с точки зрения хорошо продуманных пользовательских сценариев. И главное, с точки зрения UX. Сервис выглядел максимально простым, несмотря на закрываем сложную доменную область. При этом тот самый UX был максимально приятным и интуитивным.
Про то, что Контур на самом деле большая компания с кучей продуктов и офигенной инженерной культурой, я узнал только позже, и сегодня хочу рассказать про них и вам. Сейчас в компании больше 120 команд, которые пилят уже устоявшиеся продукты, вроде Эльбы или Контрдиадока, так и новые стартапы, так и инфраструктуру для всего вот этого. Благодаря единым понятным процессам между всеми этими продуктами сотрудникам можно спокойно перемещаться, что дает кучу возможностей для наращивания своего опыта.
А что касается технического стека, больше всего кода на C-Sharp и Python, но есть и Java и Go. Front в основном на React, но есть некоторые штуки на Vue и Angular. В плане инфры есть как собственные решения, так и стандарты индустрии. Но самое крутое в контуре, конечно, это та самая польза от продуктов, которую они развивают.
Про свою маленькую историю я уже рассказал. И работая в контуре, вы помогаете не только таким начинающим ИПшникам, как я, но и каждому третьему бизнесу в стране. Масштаб просто какой-то невероятный. Короче говоря, если вы хотите развиваться в сильной продуктовой команде, а заодно делать крутые сервисы, которые приносят ощутимую пользу, посмотрите на сайт с вакансиями, и, возможно, вы найдете что-то для себя. Ну и вступайте в их уютный канал в телеге.
где они много рассказывают про свои технологии. Теперь давай попробуем постепенно перейти к выпуску. Когда я думал про этот выпуск, моей целью было попробовать записать такой эпизод, который смогут переслушивать люди, которым надо написать какую-то библиотеку, которая будет пользоваться больше, чем один. человек.
Поэтому хочется, чтобы те, кто этот выпуск послушает, вышли с каким-то понятным представлением о том, какие вообще ограничения есть при дизайне API, каких хороших правил и практик стоит придерживаться. И вот поэтому первый... Мой вопрос — это такое про определение границ. Вот, Сев, ты как главный человек, который будет в этом выпуске разговаривать. Помоги зафреймиться. Мы говорим про такой language agnostic. или больше ориентированный на GVM-ный мир.
Мы будем в основном говорить про language-агностик подхода, вообще откуда взялся API, что его определяет, какие есть ограничения, в чем главные сложности. И местами я где-то буду рассказывать истории, как это связано с живым миром, потому что тут лежит моя большая часть экспертизы.
экспертиза заканчивается, я просто явно буду говорить, что вот в других экосистемах делают по-другому, где-то я знаю детали, где-то не знаю, где-то мы это аккуратно обойдем. Теперь, пообещав всем прикладный выпуск, погнали говорить про теоретическую часть, погнали говорить про историю в следующие два часа, как мы... В английском был такой британский ученый Морис Вилкис.
в Кембридже, кажется, в 50-х годах. Вот здесь в датах я немножко плаваю. И он, собственно, библиотеки-то не придумывал, он придумывал компьютер, у которого программы хранятся отдельно на внешней памяти. Он его успешно придумал, получил потом премию Тюрем. И на Википедии прям классный момент есть, где... И вот цитата написана, что он этот компьютер собрал...
И сразу же пошел какую-то прикладную задачу решать. По тем временам прикладная задача — это то ли дифург какой-то, то ли интегрирование какое-то численное. И что-то не работало. Результат получался не тот. И в этот момент он четко понял, что до конца жизни он будет заниматься тем, что будет искать...
ошибки в собственных программах. Он написал какое-то нетривиальное количество математических программ и через какое-то время осознал, что очень часто он одни и те же куски кода переписывает, пишет заново. Потому что не было никаких абстракций, не было никакого языка программирования. Был какой-то ассемблер, машинный код у этого компьютера. Кажется, назывался ISDAC или ETSAC, по-моему. И он поручил своему аспиранту задачу эту проблему решить.
Аспирант, значит, недолго думая, пошел работать над отдельными маленькими программами, которые можно переиспользовать в основных программах. На удивление, основная сложность там была не в том, чтобы эти программы написать. а в том, чтобы придумать способ этим маленьким программам, они назывались Сабрутина, коммуницировать с основной программой. Как и любой аспирант или стажер, он справился за лето, и, соответственно, через три месяца была готова целая...
пачка собрутин. Так как они хранились на лентах, они стояли в штуке, такой кабинет. Несколько этих кабинетов назывались библиотекой. Библиотека собрутин — это физическая конструкция. Так появилась библиотека. API здесь еще нет, здесь есть библиотека функций. Блин, офигенно. Не знал эту историю, очень классно. Тогда же они написали небольшой пейпер. Это тоже было 50-е или 60-е года, и в этом пейпере они сразу же все поняли про API и дизайн.
Значит, он двухстраничный, и там написано примерно следующее, что написать код вообще-то не очень сложно. Сложно сделать его простым, элегантным, легким к использованию, так чтобы... используя его, было сложно ошибиться, чтобы он был максимально точно и просто документирован, и при этом держа в голове то, что этим кодом, этими субрутинами, будут пользоваться люди, которые не являются экспертами в доменной области, которая этот код абстрагирует.
Они, на удивление, обошли самую сложную часть разработки библиотек — это обратную совместимость. Потому что, когда вы придумали первый компьютер, совместимыми вам быть не с чем, очень удобно. Через какое-то время, когда уже появилось больше компьютеров, больше языков программирования, программисты на фортране, здесь нет уверенности, откуда пошел оригин термина API, есть только гипотеза.
Программисты на Фортане обнаружили, что программировать мониторы и вообще графические приложения очень напряженно. Там разные вендоры мониторов, какие-то разные штуки делают, все время очень какие-то странные поведения, корнер-кейсы. И оказалось, что нужен какой-то набор функций, которые абстрагируют мониторы так, что если вы мониторы своего компьютера выдернете, а вставите другой монитор, все продолжило работать. И это называется все API. Application Programming Interface.
Это нас подводит к очень интересным свойствам, что API — это какая-то абстракция, которая отвязана от имплементации так, что эту имплементацию можно незаметно для пользователя подменить. Это произошло в 60-х. Там есть некоторый paper, который считается оригином термина API. Так и произошло. То есть API не был придуман термин API, его просто нашли. Это такое естественное продолжение, что идея-то лежит на поверхности. Мы пишем много кода, часто он повторяется.
нужно его абстрагировать. Потом оказывается, что ниже стоящие элементы, например, железо, меняется, а нам нужно, чтобы наши абстракции продолжали работать. Соответственно, API — это набор общих функций. которые определяются своим вводом и выводом, и которые отвязаны от имплементации. Интересно, что под это определение подходит примерно все в компьютерах. В общем-то, так и есть. Например, консольные утилиты.
это API, потому что у них есть какой-то способ взаимодействия. Кодировки — это API. Асемблер — это тоже API, потому что нам строгирует ниже стоящее железо. Если раньше ассемблер еще как-то мапился на инструкции ниже стоящего процессора, то сейчас ассемблер — это такая абстракция над виртуальной машиной, которая притворяется, что она процессор из прошлого поколения. На самом деле современные процессоры работают совсем по-другому.
Даже структура файлов в Linux, где какие файлы лежат в Linux File Tree, это тоже API. Это такой клей между разными слоями компьютера, который в основном занимается тем, что обманывает следующий слой, чем он на самом деле является. Очень удобно. Окей. С тем, как API появился, мы разобрались. С тем, что такое API, разобрались. Как вообще практики дизайна API в истории развивались? В истории было очень интересно. Был Unix, и было много разных вендоров.
И они все, значит, делали какие-то свои примочки, свои расширения, свои функции. И на самом деле это не очень удобно. Потому что получается, что сильно привязано к конкретному производителю, конкретной операционной системе. Там еще контракт часто с конкретным железом идет. Не очень удобно. И американская военка быстро все решила и сказала, ладно, ребята, хватит. Значит, вот мы сейчас придумаем стандарт. Назвали его потом Possex.
И мы не будем покупать софт у тех, кто этому стандарту не следует. Очень просто решили проблему, и получается, что есть много вендоров чего-то очень похожего, и надо, чтобы софт между этими вендорами был переносим. И тут как бы влезает одна из первых сложностей API-дизайна. Это человеческая природа. Человеческая природа тут заключается в следующем. Что если вы большой вендор, вы можете полностью комплинить стандарту. Но если у вас нет никаких примочек...
у вас нет никакого способа залогкинить вашего клиента на ваш контракт навечно. Потому что он в любой момент может перейти, например, из AT&T в Sun Microsystems. Примерно так и появилось. Часто был какой-то большой заказчик, большой клиент, который все это утомляло, и нужен был какой-то общий язык, чтобы с нежестающей системой разговаривать.
Где-то разработчики сами это поняли. Например, разработчики C очень быстро поняли, что чтобы C стал успешным, им нужен какой-то способ общения с операционной системой, от этой операционной системы отвязанной. Так, собственно, появился ли C. Они тоже довольно четко отдавали себе отчет, зачем это нужно.
Я думаю, мы сегодня еще не один раз будем отсылаться на другие наши выпуски, но Сева сказал Си, и я не могу не прорекламировать отличный, шикарный выпуск про Си, который мы записали на момент выхода этого выпуска несколько месяцев назад. Обязательно послушайте. Слушайте, прямо, короче, бомба. Сейчас позвольте на секунду прервать ведущих и гостя, чтобы сделать небольшой анонс. Если вдруг пишете на го или интересуетесь этим языком, не проходите мимо.
28 октября стартует очередной сезон нашей онлайн-конференции «Подлодка Го Крю». И в этот раз мы делаем ее в партнерстве с Deck House. Подлога GoCrew — это недельная конференция для гошников, и тема этого сезона — архитектура. То есть всю неделю мы будем говорить так или иначе про архитектуру и то, как положить ваш код на бизнес-процессы. И здесь будет много всего разного в программе.
Начиная от того, как применять подход BDD, например, для трансформации бизнес-требований в тесты и, допустим, про генерацию кода из спеки и генерацию спеки из кода, поговорим про всякие разные дизайн-паттерны вроде, например, саги.
или поговорим про декораторы, архитектуру тестов. В общем, будет много всякого разного, если вы представляете всякие базворды вроде там DDD, dependency injection, вот это вот все. Это все тоже так или иначе будет. В общем, будет много всего интересного. Ну и, как всегда, мы делаем фокус на том, чтобы вы смогли применить... полученные знания в своей работе ну практически сразу же и как всегда для слушателей нашего подкаста скидка
по промокоду GO, нижнее подчеркивание, LIP. GO, потому что понятно почему. LIP, потому что вы слушаете выпуск про библиотеки. Так что еще раз напоминаю, 28 октября подлодка GO Crew. Тема сезона архитектура, скидка по промокоду. Все ссылки и промокод будут в описании. А я возвращаю слово ведущим подкаста. Окей.
Ты, когда рассказывал про то вообще, как появился термин «библиотека», сказал, что, собственно, авторам первых сабрутин не нужно было решать одну из самых больших проблем в дизайне библиотек — это обратную судьбу. совместимость. Можешь чуть побольше рассказать вообще про проблему, в чем ее суть, когда в истории они начали вообще в целом задумываться, как это сказалось на практиках дизайна API? Могу рассказать анекдот вместо этого? Отлично. Живут студенты в общаге.
и студенты варят сосиски. Студент достает пачку сосисок, достает сосиски, отрезает у них кончики, начинает их варить. Второй студент своего приятеля спрашивает, слушай, а зачем ты, собственно, кончики у этих сосисок отрезаешь? Я не знаю, у меня папа так сосиски всегда варил, вот и я.
варю. Ну, значит, звонит своему отцу, спрашивает, пап, почему ты отрезал кончики у сосисок, когда их варил? Папа отвечает, да слушай, у меня так мама всегда варила. Я не знаю. Давай у нее спросим. Значит, звонит своей бабушке и спрашивает. Бабушка, а зачем мы кончики-то у сосисок отрезаем, когда их варим? На что бабушка восклицает, вы что, до сих пор варите сосиски в моей маленькой кастрюльке? В общем-то, backwards compatibility, оно про это.
что несмотря на то, что API отвязан от ниже стоящей системы, в нее так или иначе протекают какие-то детали реализации или ограничения физического мира или этой ниже стоящей системы. Если API по-настоящему успешен, у него много пользователей, гигантское количество программ или какого-то софта, написанного поверх этого, то этот API переживает систему, поверх которой он изначально был построен. Ограничения искусственные и никуда не деваются.
Самый простой пример — это Windows при переносе строки вставляется slashr slashn в качестве символов сепаратора между строчками. Сделано это потому, что у предшественника Windows DOS был предшественник, кажется, CPM или какая-то такая машина, у которой был целетайп, у которой эту кнопку отдельно нужно было нажимать. Поэтому это было отдельным символом. А так как поверх DOS, соответственно, был с этой...
системой совместим, Windows совместим с DOS, и вот как бы прошло сколько лет, уже никто не знает, что это был за компьютер и что это были, значит, за внешние телетайпы, а Slashare остался. Итак, в среднем... происходит по гигантскому количеству популярных API. Наверное, API, который affected, с которым связано больше всего людей, это POSIX. Там все имена очень короткие. Там вместо create, например, create без и на конце. Там есть RAM вместо remove.
Причина у этого очень странная, что когда POSIX разрабатывался на каких-то IBM-ов... Кастрюлька маленькая была. Да, у IBM-овских машин был какой-то линкер, который не очень хорошо работал с символами, с файлов длиннее шести или восьми символов, а там еще .sirus. ширение. Поэтому и был такое искусственное ограничение. С тех пор, собственно, таких ограничений нет, но зато существует гигантское количество софта, и ладно, еще софт, существует гигантское количество...
памяти поколений, гигантское количество образовательных ресурсов, книг, курсов, университетских программ, где уже такие концепции используются. Все знают, что нужно, чтобы удалить файл, писать RAM. Если это вдруг поменять на remove... то, значит, 40 лет или 60 лет памяти поколения, оно закончится. И все будут по привычке писать RM, старые программы перестанут работать, все будут несчастны.
Поэтому вот у нас есть короткие названия программ, короткие названия функций. Но можно же какие-нибудь алиасы добавить, то есть нельзя вечно тащить это наследие. Можно же, чтобы и remove, и rm работало. Почему так нельзя решить вот все такие проблемы? Можно, тогда все такие проблемки просто удваиваются в своем количестве. RAM никогда не получится удалить. Уже существует критическое количество софта, с которым ничего не поделать.
и его уже не переписать. Возможно, у него уже исходные коды потеря. Вот, например, одна из моих любимых игр Fallout. До сих пор можно скачать и поиграть в нее. Проблема в том, что у нее исходный код потерян. Ее уже не пересобрать никак. Если вдруг мы когда-нибудь захотим эту... функцию rm удалить, оставив только remove,
то она перестанет работать. А если ее оставить, у нас теперь есть и RAM, и REMOVE, и во всех книжках еще нужно будет писать. Кстати, вот есть REM, но это не то, что вам нужно. Вам нужно REMOVE. Хотя REM делает то же самое. Если они начнут отличаться, то проблема как бы еще сильнее начинает вставать штыком, потому что есть две функции, которые делают почти то же самое, но разные. Соответственно, с обратной совместимостью история довольно тяжелая.
что ее надо тащить через года, и эффекты аукаются очень сильно. Например, по последним оценкам, от 5 до 15% всего трафика в интернете тратится на заголовки Ethernet, у которых сильно ограниченная длина. Ограничена она потому, что когда этот протокол придумывали, был другой токен-ринг, который по общей сети работает. Если бы заголовки были слишком большими, он бы не...
смог быть с ним совместим, занимал бы слишком много пропускной способности общего коаксильного кабеля. С тех пор ничего из этого правдой не является. Но есть миллионы задеплоенных роутеров, свитчей, а у них уже есть захардкоженный лимит длины заголовка. Так и живем. Слушай, а знаешь ли ты какие-то примеры, когда все-таки от такого легоси получалось каким-то образом избавиться? То есть кто-то делал такое решение?
взмах рукой и говорил, ну и черт с этим фоллаутом, пусть дальше не работает, зато в будущем всем тем бесчисленным поколениям разработчиков, которые придут после нас, будет хорошо. То есть делал ли кто-нибудь такое? Есть разные способы решить эту проблему.
регулярно грешит, или наоборот, регулярно так делает Apple, у которого закрыта экосистема, у нее нет альтернатив. Соответственно, они могут обратную совместимость сломать как угодно, если от этого страдают программисты и разработчики, а не пользователи.
Поэтому они периодически переезжают с одной микроархитектуры на другую без миграций. Иногда они из последнего большого, они, собственно, задеприкетили и вывели из использования 32-битной библиотеки. И это как раз сломало все старые игры, которые раньше можно было...
какой себе запускать. Некоторые языки программирования или фреймворки просто выпускают следующую версию. Часто к ней приписано, например, V2 или какой-нибудь такой аналог, где они... решают все предыдущие проблемы, все проблемы предыдущего решения, но являются несовместимыми с им.
Но в по-настоящему успешных API такое сделать просто невозможно. Окей, еще один вопрос рядом с обратной совместимостью. Если смотреть на проблему с точки зрения человека, который дизайнит API, как все-таки делать выбор между сохранением обратной совместимости и ломанием стандарта? Ты понимаешь, что ну хреново что-то задизайнено, плохо пользователям, но поменяешь, все сломаешь, все будут плакать. То есть где вот эта грань? Надо сохранять обратную совместимость. Я вот не забуду, как я...
Со свифта на второго на третий переходил. Егор, ты переходил? Это ты еще на шестой не переходила со strict concurrency. В общем случае, надо просто сохранять обратную совместимость. Потому что если вы популярная платформа, будь то WinAPI, линуксовые секс-колы, Java-платформа, Kotlin-платформа, C++ АБИ вы не можете сломать весь тот гигантский мир, который уже существует. Это просто создаст огромные сдержки для всех участников всего мероприятия по апдейту вашей платформы.
Если проблемы стоят совсем остро, например, вы по какой-то причине знаете, что, не знаю, 20% пользователей вашего API все время ошибаются. или что API в конкретной форме — это такой мультипликатор security уязвимостей. Вы старый API пытаетесь любыми платформенными способами задеприкейтить и вывести из использования, при этом удалить вы его не можете, а добавляете вместо этого новый.
Либо это будет какой-нибудь, например, в случае посекса, что-нибудь с суффиксом нижнее подчеркивание R, либо это будет целый новый слой API, а старый просто останется гнить, и потихоньку может быть поддерживаться, может быть вообще никто его трогать не будет. Обычно поступают так. Обратную совместимость лучше не ломать примерно никогда.
Отличные истории ты рассказывал с примерами и про обратную совместимость, и про другие аспекты. Если у тебя еще байки в запасе про то, как разные исторические особенности эволюции... вообще влияют на дизайн библиотек в настоящем. Да, наверное, прям больших нет. Из интересного, например, есть OpenCV, популярная библиотека для работы с компьютерной графикой. Так вот она все цвета пикселей принимает не в RGB, а в GBR, потому что тогда...
как раз были девайсы, которые работали в такой колоринг-схеме. С тех пор, естественно, они померли, а колинг-конвенция осталась. В общем, так и живем. Еще из прям совсем интересной байки про то, как причины какого-то решения могут потеряться в истоках. Есть такой оператор над коллекциями или над потоком данных в языках программирования Reduce. У него есть альтернативная версия Scan. которая идет, соответственно, по всем элементам и выдает частичные результаты на каждый элемент.
Это пришло из реактивных фреймворков. Мы когда его в Kotlin, в стандартную библиотеку, добавляли, мы его так и назвали — Scan. Потому что люди, которые пришли из реактивных фреймворков, уже знали, что это. А других хороших названий мы не нашли.
Мы это выпустили как экспериментальный API, который как раз можно ломать. И где-то на Reddit кто-то написал довольно подробный пост и пришел к нам в бактрекер и сказал, что название скан вообще-то абсолютно непонятное, ничего не отражает, и назовите, пожалуйста, получше.
И мы всерьез посмотрели на его аргументы. Во-первых, это был тот самый случай, когда пользователь пожаловался на именование, и мы это переименовали в runningFault и runningReduce, соответственно. Но еще интересно, почему оператор-то так называется, Scan. Я пошел ковырять.
В пейперах ссылались на язык B, кажется, на язык B, а оттуда на язык APL, который A Programming Language. У него есть спецификации, там действительно был оператор, который выдавал частичные суммы, назывался SCAN в документации, но... Функции у него не было, у него был такой просто слэш. Я решил написать авторам, собственно, почему они его так назвали. Я открыл список лид-лангивич-дизайнеров, там было три штуки. В общем, два из них умерли на тот момент, а третий неизвестно где он.
Он ушел на пенсию, его нет в интернете, его нет в Фейсбуке. Он был только на форуме Бернинг Мэна. Я, значит, зарегистрировался на форуме Бернинг Мэна, написал ему, но, к сожалению, он мне не ответил. Сейчас, просто представьте, через 30 лет кто-то раскапывает котлинскую стандартную библиотеку и такой first note null, offer null, нам нужно найти автора. Выезжаем на Берлингбэн. Но вообще, мне кажется, ожидать от авторов языка с названием e-programming language хорошего объяснения.
почему их функция названа так или иначе, возможно, и не стоила. Ну, стоило попытаться. Попытка не увенчалась успехом. Ну, байка уже достойная, мне кажется, outcome. Там есть гипотеза. Мне кажется, мы нашли ответ, что это на самом деле просто scanline, и оператор-то, собственно, выглядит слэш, как линия, и оттуда и название. Но уверены, мы быть не можем. Но мы прервали этот порочный круг, и больше у нас нет оператора scan. Так, порочные практики и победим.
Про историю поговорили, давай немного поговорим про то, а в чем вообще сложность задачи дизайна API, помимо всего, что ты уже перечислил. Почему это вообще сложная задача? Самая сложная проблема, может быть не самая сложная, одна из самых сложных, это обратная совместимость. Потому что добавив что-то... в API. Если это стабильный API, удалить это уже невозможно. И починить или поменять это стоит огромных денег. Например...
была такая знаменитая проблема. Проблема 2000 года, когда были две цифры для номера года в 20 веке. И в 2000 году этот счетчик, соответственно, переполнялся. И теперь, например, число 01 могло означать как 1901 год, так и 2001. Чтобы починить эту проблему, по разным оценкам, это рекламировалось и анонсировалось как большая катастрофа, что гигантское количество софта, всяких таблоидов, расписания поездов, самолетов, весь этот софт должен был сломаться, его должны были заменить.
Какие-то космических денег, по оценкам Соединенных Штатов, заняло нам что-то порядка то ли единицы триллионов, то ли десятки триллионов долларов. Понятно, откуда госдолг взялся. Какие-то просто невозможные числа. И тут интересно, что если удалить это сложно, то это, к сожалению, означает, что вообще подход к разработке API встает в конфликт с тем, как мы обычно пишем код. Потому что если мы пишем прикладную программу...
Но мы в нее что-то добавили. Если нам это не нравится, мы это удаляем оттуда. Или рефакторим, или меняем поведение. Или если нам имя вдруг не нравится, мы это переименовываем. И это в целом такой общий подход. Например, подход Фейсбука, суперизвестный Move Fast and Break Things, с API просто не работает.
И если мы перейдем на чуть более такой неформальный уровень, то есть такая концепция — технический долг. И долг — это вообще штука хорошая. Брать в долг полезно, потому что если мы за это получаем больше бенефита, чем платим процентов...
то мы остаемся в плюсе. Например, бизнесу срочно нужна какая-то фича, но мы открыли pull-request без тестов, что-то особо не подумали, замерзли, выпустили релиз, набежали пользователи, покупали нашего продукта, все довольны, а мы потом отдельно запланировали какой-то... кусочек, где мы этот технический долг починим, добавим тестов, если найдутся какие-то проблемы, починим.
Если вдруг эта штука ведет себя некорректно, мы это исправим. С API так не работает. Если вы вдруг быстро начинаете программировать и выкатывать, то либо вы это постоянно ломаете, постоянно меняете, постоянно ломается, все недовольны. и в итоге пользователи от вас начинают уходить, или перестают вами пользоваться, или пользуются, но очень недовольны, как в случае, например, со свифтом. Либо у вас появляется такое гигантское-гигантское наслоение. То же самое касается и более...
аккуратной разработки, что обычно все наше программирование тактическое. Если у нас уже есть какая-то система, и нам нужно что-то сделать, мы добавляем еще одну фичу, она, может быть, как-то с другими будет взаимодействовать, может, не будет, нас не очень интересует, главное...
тимминутный результат получить. Потом вскрывается, что наша новая фича очень плохо взаимодействует с какой-то другой древней фичой. В этот момент мы это уже поправить не можем, если мы говорим про API-дизайн. Если мы говорим про обычную разработку, можем.
Соответственно, инкрементальная сложность накапливается и накапливается. В случае обычной системы ее можно переписать, ее можно выкинуть, можно ее заменить на что-нибудь другое. Можно какой-нибудь large-scale maintenance завести. В случае с API у нас просто будет API с гигантской сложностью.
И ничего мы с этим поделать не сможем. Подожди, но сейчас... Короче, давай я тут с тобой поспорю. Ведь, кажется, человечество в какой-то момент посидело, подумало, поскребло в голове и сказала, что окей, мы... Ломать-то совместимость можем, но в том случае, если для пользователя этого API есть какой-то понятный контракт, когда эта совместимость сломается. И придумали концепцию под названием Semantic Versioning. И сказали, что у нас будут номера версии API.
Когда меняется маленькая цифра, мы ничего не ломаем. Когда меняется средняя цифра, мы, я не помню, ломаем не сильно, а когда меняется большая, ломаем все к чертям. Это хороший вопрос. Семантик вершининг — это идея, которая на бумаге звучит просто...
восхитительно. Значит, там есть три компонента версионирования. Это патч-версия, где мы можем только не ломающие бэкфиксы делать. Это минорная версия, где мы можем обратно-совместимым способом добавлять новые фичи. И мажорная версия, где мы можем ломать. И тут уже в самом определении есть проблема, что мы что-то можем ломать. Если у нас есть гигантское количество пользователей...
От того, что мы сказали, что мы это сломаем, и мы это сломали, никто счастливее не стал. Если у нас уже есть миллион пользователей, все на это позависели, а мы это сломали, хотя там было написано, что мы это можем сломать... Все остались недовольны по итогу. Пользователи не мигрируют на новую версию. Пользователи, может быть, мигрируют со старого инструмента на какой-то другой.
Пользователи долго сидят на старой версии. Например, 3-й Python или Scala 2.11 — это хорошие примеры такой истории, что формально мажорная версия языка могла бы все сломать. Но оказывается, что если вы все ломаете, то первичным является не язык программирования, не его API, а все то, что построено поверх него. И...
Экосистема не мигрирует. Ну, сейчас, так как? Что, в итоге все нормально с Питоном 3? Ну, в смысле, шутки шутками, шутки шутками, но как? Итог-то все в порядке, избавились от груза. Это заняло примерно декаду переехать, и гигантское количество... денег и времени. Кроме этого, собственно, если возвращаться к Симверу, подразумевается, что изменения делятся на неломающие и ломающие. И если вы, значит, обновляетесь на версию, в которой...
в мажорной цифре ничего не поменялось, то у вас нет ломающих изменений. На деле, примерно любое семантическое изменение ломающее. Если вы кидали один тип исключения, а начали кидать другой, и у вас в документации это никак не отражено, пользователь, скорее всего, все равно на это зависит. Вообще, программист очень креативен. И если у них есть какая-то своя прикладная задача, они сделают все, чтобы ее выполнить.
А то, что написано в контракте или в документации, их волнует в последнюю очередь. Возможно, они ее даже не прочитали, потому что у них была своя собственная работа быстро сделать какую-нибудь фичу, например, или какую-нибудь новую крутую штуку. Поэтому, подразумевая, что есть ломающие и не ломающие изменения, это небольшой обман. Любой багфикс — потенциально ломающие изменения.
Ты точно должен знать, есть же даже какой-то закон или эмпирическое правило имени кого-то, что если там у тебя хоть что-то торчит в API, даже если не публично, то кто-то обязательно позависит. Я знаю, как оно пишется, я не знаю, как оно произносится. Наверное, это Hyrum Law. H-Y-R-U-M. Соответственно, оно означает примерно следующее. Что какими бы свойствами ни обладала ваша система...
документированными или недокументированными. Всегда найдется кто-то, кто на эти свойства уже зависит. Даже на XKCD есть популярный комикс, там, значит, патчноута ЕМАКСа, где написано, что мы подчинили проблему, когда зажимание пробела... Случайно уходила в бесконечный цикл и перегревала процессор. Нам пользователь пишет, что вы знаете, у меня Ctrl на клавиатуре находится очень далеко.
И я настроил в своей операционной системе так, что быстрый скачок температуры — это нажатие Ctrl. Поэтому мой воркфлоу после вашего обновления сломался. Почините, пожалуйста. И комикс, он как бы отражает реальность. Что бы вы ни сделали, на это все зависят. И сломать вы это, скорее всего, не можете. И даже поменять это довольно сложно. Даже если это какие-то совсем нюансы реализации.
Наверное, из самого большого из последних, ребята из Гугла, у них был большой блокпост про то, как они пытались ускорить сортировку в стандартной библиотеке C++ в LLVM где-то. И сортировка вообще штука довольно хорошо определенная. один элемент либо меньше другого, либо больше другого, либо равенному. Казалось бы, если сортировка стабильная, для одинаковых элементов сохраняется порядок.
то все хорошо. Но люди зависели на такие детали реализации, как сколько раз вызовется компаратор, сравняться ли два одинаковых объекта. Для нестабильной сортировки они все равно как-то неявно зависели на неявный порядок. одинаковых элементов. И в итоге, чтобы даже такое изменение провернуть, которое вообще по симверу попадает в минорную версию. Это никакой не breaking change. У них, кажется, два года заняло.
Возможно, три. А говорят, программирование — это не творчество. Вот какими можно творческими пользоваться историями. Вопрос у меня такой был. Если Semantic Version не так уж и хорош, но если, я не знаю... какой-то альтернативный подход к решению проблемы обратной совместимости, к тому, что так или иначе, к сожалению, реальность такова, что идеально спроектировать с самого начала, чтобы не захотелось потом что-то менять, сломать, но как будто бы...
Невозможно, хотя очень хочется. Или это просто, типа, меньшее из зол? Я бы не назвал это меньшим из зол. Это скорее наоборот. Симвер — это такая попытка закрыть глаза на все это. Потому что, ну...
Как будто бы это поделение, что с минорными версиями можно делать что угодно, с мажорной версией все ломают и дает карт-бланш на поломке. Как бы не то. Глобально ломать вообще ничего не нужно. В патч-версиях обычно добавляются... нестабильный API, или делаются такие баг-фиксы, которые, очевидно, неверное поведение чинят, чтобы пользователи, которые не особо думают про то,
что находится в номере патч-версии, могли не думать о том, какая именно версия выберется, и не зависеть на этом. То есть они зависят, например, на версию 2.5, 2.5 что-нибудь, и чтобы при... изменение 2.5.1 на 2.5.2, у них ничего не сломалось. Так, я, короче, хочу еще немножечко здесь покопать. Если мы сравним создание библиотек с реальным миром, то в реальном мире у всяких физических продуктов, например, у автомобиля, у, не знаю, телевизора или у телефона...
Есть такая штука, как гарантия. Когда он ломается... и ты его использовал по предназначению, производитель тебе говорит, да, это я дурак, держи, короче, новую машину. Но если оказывается, что ты использовал что-то не по назначению, и оно сломалось, и ты недоволен, тебе говорят, дружище, ты сам дурак.
Почему в случае с развитием API-библиотек должно работать по-другому? Если кто-то зашился на то, сколько раз вызывается компаратор при сортировке, а это недокументированное свойство, на которое у тебя не было контракта, его поддерживать, почему ты этому пользователю... что-то должен. Сломалось? Ну, блин, дружище, извини. Ты хреново... Ну, программисты так себе. Раз на это зашился, расплачивайся. В будущем не будешь таких ошибок совершать. Типа, зачем?
Если глобально, потому что это дискредитирует экосистему. Конечному пользователю... конечных приложений, ему не очень интересно, кто виноват. Если у него всплыла плашка, не хотите ли вы обновиться на новую версию операционной системы? Она там, соответственно, обновила новую версию операционной системы, какой-нибудь Lipsy, VLC Player, какую-нибудь программу для стрим...
видео и записи, и оно перестало работать. Пользователю неважно, кто там виноват. Программист, который воспользовался незадокументированным контрактом. API-дизайнер, который дал очень странный или недостаточно сильный контракт, и не стал какие-то варианты проверять. В конечном итоге...
У пользователя ничего не работает, пользователи с этой платформы уходят. Соответственно, в интересах разработчиков программного обеспечения пользоваться только документированным API и только документированными контрактами. А разработчикам этого самого API, это нежестоящая платформа, в их максимальных интересах делать так, что если вышестоящий слой все-таки на что-то странное позависел, их не разламывать.
В этом плане есть Линус Торвальд с его очень жесткой политикой «We'll never break user space», которая как раз про это. Что да, программисты иногда делают странные вещи, да, программисты иногда зависят на какие-то странные контракты, которые не были прописаны в документации. Иногда это даже случается неявно. Просто так получилось, что все числа в программе оказались положительными. И никакой код ни на какие отрицательные числа не рассчитывает. Если вдруг это где-то начинается, даже...
несмотря на то, что нигде ни в одном контракте не было написано, пользовательские программы ломаются. И это еще одна сложность на самом деле разработки API. Программисты, ну вообще на самом деле люди, люди плохо оценивают вероятность маловероятных событий. Например... люди плохо оценивают вероятность того, что время начнет течь назад. Потому что мы обычно об этом не думаем. Но если вдруг...
В какой-то момент у вас где-то на НТП-сервере поменялось время, откатилось на час назад. Это приехало на ваш компьютер. Или случилась просто високосная секунда. И где-то ваша программа вдруг откатилась по времени, вы... Посмотрели на таймстэмп, что-то поделали, посмотрели на таймстэмп.
Вычли один из другого. Получилось отрицательное число. Вы проиграли. Окей, кстати, про дату и время. Опять же, если захотелось побольше баек, есть выпуск с Тонским. Отличный про дату и время. Тоже обязательно послушайте. Выпуск шикарный, и там... как раз затронута одна из проблем API-дизайна, это в том, что человеческий язык сложный и неоднозначный. И очень сложно понять, что же мы имеем в виду. Например...
Мы договариваемся о времени записи подкаста или когда мы его начнем. И мы договариваемся через час. Через час — это более-менее физическое время, фиксированный промежуток. Но если мы говорим, например, через месяц, и мы это говорим... не знаю, 30 января, то через месяц это когда? И тут оказывается, что наш язык, наш подводишь, он дает неточное определение. И в программировании то же самое. Например, вы пишете функцию, которая парсит из строки число.
И вы пишете, что функция возвращает ошибку, если символ не является валидной цифрой. И вы прекрасно понимаете, что вы имеете в виду. Что если символ — это обычная цифра от 0 до 9, то программа корректна, иначе возвращается ошибка. Но в Unicode, например, есть специальный символ для арабской девятки вообще.
В Unicode есть очень много разных цифр, и формально попытаться понять, что же именно имел в виду автор, очень сложно. В интернете есть очень классный тест, который называется No Vehicles in the Park. Тест говорит про следующее. Есть парк, есть правило. Нельзя брать с собой транспортное средство в парк.
И дальше список из какого-то количества вопросов, которые сформулированы так. Мальчик взял радиоуправляемую машинку и заехал на ней в парк. Нарушил ли он это правило? Над парком прилетел самолет. Нарушил ли самолет это правило? Это на самом деле такая проблема. общечеловеческая, и законов, и языков программирования, и API-дизайна, что мы имели в виду одно, а люди интерпретировали это совсем по-другому. Так, а есть ли вообще какие-то критерии того, что можно считать хорошим API?
Если поместить людей в МРТ-машину и показывать им разные искусства, и спрашивать, понравилось им оно или нет, там разные куски мозга активируются. Вот в тех случаях, когда искусство им нравится... активируется кусок мозга, который отвечает за то, что мы хотим что-то съесть, когда мы это видим.
А когда не нравится тот кусок мозга, который отвечает за реакцию «бей или беги» за кусочек «беги». Соответственно, хороший API — это когда хочется съесть и не хочется убежать. А если серьезно, то хороший API... Это примерно как хороший продукт. Хороший телефон, хороший компьютер, хорошая программа. Там все определения очень, очень вейк, очень... недетерминированно, что он решает понятную проблему. Пользователям не нужно учиться им пользоваться, то есть они его узнают, а не учат.
Хорошим API сложно воспользоваться неправильно, легко делать простые вещи, возможно, делать сложные. Он абстрагирует много, а предоставляет мало. При этом еще надо, чтобы он был тестируемым, чтобы его можно было легко отлаживать. И легко объяснять другим людям. И не ломается при переезде на новую версию. Ну, это мы уже выяснили. И не ломается на переезде на новую версию. Все правильно. Окей, есть ли еще какие-то проблемы реального мира, которые...
не дают вот этих замечательных свойств на практике добиваться. Да, люди в основном. Главная проблема во всех проблемах программирования. Например, из последнего... Мы для нашей конференции пытались сделать небольшой API, который как-то раскрашивал контурные карты на экране. И API был такой, что вы подаете туда код страны и цвет. И этот цвет подсвечивается. И этот API, он публичный, он доступен пользователям. И тут вопрос. Тайвань — это страна или не страна?
Оказывается, что ответы на этот вопрос не то, чтобы его можно было легко дать. Более того, некоторые вендоры специально в разных странах по-разному работают. Например, в Unicode есть флажки стран. Там есть прям специальный плейн с флагами, с флагами стран. И iPhone их отлично рендерит. Но если вы вдруг уедете в Китай и...
достанете там айфон и попробуете там отрендерить тайваньский флаг, как флаг страны, окажется, что это квадратик, который заменен на несуществующий символ. Таймзона — это очень хороший пример. Причем с самых разных сторон. Например, в 2011 году или в 2012 в Египте были какие-то острые политические события, там были две правящие партии, одна ввела переход на летнее время.
другая не ввела, потом какая-то одна из них победила и ретроспективно его отменила. Что в этот момент делать разработчикам библиотеки даты и времени, очень понятно. Наверное, не работать с датами в Египте в это время. Иногда API завязан на людей. Это, наверное, самый выдающийся пример, это тоже дата и время, это Япония. Там календарь Григоянский, там летоисчисление называется по тому, как его назвал новый император.
И недавно, пару лет назад, старый император Японии сказал, что-то я устал, ребята, в общем, вот мой преемник, а я пошел на отдых. Новый преемник вышел и сказал... Значит, новое летоисчисление, новая эпоха, называется как-то там эпоха благоденствия и цветущих цветов. И вдруг оказалось, что всему софту надо быть к этому готовым. И, например, Java, которое мало того, что надо возданных часовых поясов обновить, Java это надо в программном API.
поддержать. Поэтому там Java за выходные очень быстро выпускала hotfix-релизов, потому что следствие того, что это не поддержано, это, например, какая-нибудь банковская система и система документа оборота, где нужно ставить даты в Японии, просто перестанет работать. Зато император новый и все хорошо. Да. Так.
Про то, что такое хороший API, не очень понятно. Мы поговорили. Про проблемы мы поговорили. Ну а делать-то что вообще с этим? Как в том мире, где абсолютно все проклято, где без хорошего API... API не выжить, но хороший API, кажется, не задизайнить, как все-таки, ну, типа, сделать good enough API. Главное — смириться с тем, что отлично не получится. Если вы успешно задизайнили API, это значит, что пройдет год, два, три...
и вы будете очень недовольны и будете жалеть о большом количестве решений, которые вы приняли. Это значит, что он успешен, им все пользуются, вы хорошо сделали свою работу. Тут важно понимать, что есть много разных плоскостей. в которых можно думать. Например, хорошая плоскость — это определить абстракцию. Потому что, когда вы делаете API, вы абстрагируете что-то чем-то другим. Например, какой-нибудь API-файловая система, он на самом деле абстрагирует очень...
API, работа с файлами, абстрагирует очень много вещей. Он абстрагирует операционную систему, он абстрагирует драйвер конкретной операционной системы, он абстрагирует блочный девайс, который, значит, по нужным афсетикам. пишет эти файлики. Этот блочный девайс абстрагирует контроллер диска, который тоже со своим API работает. И там где-то внутри есть диск, который как хранит байтики, как именно мы до конца не уверены и, возможно, уже не так, как, значит, это...
торчит в драйвере, но абстракция хорошая. Иногда абстракции бывают плохими и сильно текут. И тут важно, есть абстракция, есть иллюзия. Иллюзия — это абстракция, которая скрывает важные имплементационные детали. Например... примерно раз в 10, в 15, в 20 лет индустрия приходит к такой штуке, как RPC. Remote Procedure Call.
Кажется, что это отличная идея. Например, когда-то в 90-х придумали NTFS, удаленную файловую систему. Когда у вас есть файловая система, она хранится где-то, возможно, на других компьютерах, возможно, на многих компьютерах. Но для вас, для пользователей, это такие локальные файлы. Идея... супер классную, что есть какая-то очень сложная вещь, которая абстрагирована привычным интерфейсом. И на поверхности все хорошо. На практике оказывается, что это не работает примерно никогда.
Потому что вдруг появляются такие проблемы, как latency, что записать 10 раз по одному байтику и записать за раз 10 байтов — это фундаментально разные конструкции в такой модели абстракции. Или существует такая вещь в распределенных системах, как частичные отказы. Когда вы, например, послали команду «Создать файл». Эта команда вернула вам ошибку. Файл создать не получилось, потому что произошел таймаут в сети.
Но вы об этом не знаете, потому что файловая система API файлов не говорит им, что произошел тайм-аут, просто не получилось создать файл. Но при этом команда создать файл дошла, а ответ, аналлажмент на то, что создать файл, она не дошла до компьютера, и вы потом...
Пытаетесь создать этот файл еще раз, а вам файловая система говорит, а такой файл уже существует. Соответственно, такая абстракция вдруг начинает очень сильно течь. И у таких абстракций есть много трейдов. И тут, например, можно сыграть в игру. интерактив. Есть такая концепция в Linux, что все есть файл. И Linux некоторые свои мониторинги или информацию о своем состоянии выдают как файл. Например, есть PROCFS. Специально примаученный раздел файловой системы PROC.
slash, значит, ID-шник процесса, slash, и там есть разные файлы, можно из них чего-нибудь про конкретный процесс почитать. Идея суп тоже хорошая, что у вас все есть файл, вы знаете, как работать с файлами, вы в любой момент можете его прочитать. Записать в них нельзя файлы read-only. Все отлично. Вопрос. Хорошая ли это абстракция? Есть ли у нее какие-нибудь недостатки? Так, но очевидно, что если бы это была хорошая, отличная абстракция, мы бы сейчас ее не обсуждали, наверное. Наверное.
Кать тебе передаю. Ты украл мой ответ. Ну давай подумаем, где она может течь. Давайте я могу позадавать вам наводящие вопросы. Представьте, вы пишете программу... которые, например, в пользовательском интерфейсе показывают какую-то статистику по процессам. Сколько памяти они потребляют, сколько процессора они потребляют. Вам нужно как-то программно распарсить эти данные. В каком формате эти данные хранятся? Где это определено? В файле.
Ну, видимо, в том, как структура файловой системы устроена здесь. То есть, если ты сказал, что у тебя каждому процессу премаунчен какая-то папочка, условно говоря, то тебе надо посмотреть на структуру этих папочек, и вот у тебя всесвязь. есть список процессов. А в папочке лежат файлы. А вот в файле лежит JSON, лежит XML или лежит что-то другое?
Наверное, есть стандарты. Где это написано где-то, но это где-то отвязано от места, где вы это читаете. У вас нет API над этим. То есть у вас есть API, что вы можете прочитать это? Но когда вам нужно получить программное представление, у вас нет никаких гарантий. Вам нужно пойти в какой-то third-party ресурс, man pages, прочитать какой-то текстовый...
структурный формат, и начать его руками парсить. Это, например, один из недостатков. Другой из недостатков — это, представьте, что потенциально файл большой, и вы его начинаете читать. А в этот момент что-то поменялось. Например, процесс стал потреблять больше памяти. Есть ли у вас в этот момент какие-то гарантии?
На то, что вы прочитаете, соответственно. Ну, видимо, нет. Там... Ну, от локов зависит, видимо. Если ты поставил какой-то лок на файл, когда его читаешь от изменения, видимо, у тебя есть тогда какая-то гарантия. Есть гарантия, если вы читаете все одним запросом специальным системным... вызовом, который читает все одним блочным запросом. Это что-то, про что нужно подумать. Дальше следующий вопрос, как бы утверждение скорее, что есть процесс, он что-то делает, и вы хотите узнать.
сколько памяти он потребляет, и записать это в какой-то лог-файл. И вы узнали ID-шник этого процесса и пошли читать этот файл. А в это время процесс умер, а процесс ID переиспользовался. И у вас как-то, получается, нет способа, значит, понять, чью же статистику вы прочитали. Там, естественно, в какой-то момент добавили очень разные там PDFs и какие-то штуки вокруг. Вот здесь вот я плаваю, потому что активно этим не пользуюсь.
Но это такая текущая абстракция. Она очень удобна для одних примеров использования и для одних юзкейсов, а для других неудобна. Она идет с некоторыми трейдовами. Соответственно, абстракцию нужно уметь выбирать. Это то место, где в первую очередь нужно задать несколько вопросов. Получается ли абстрагировать глубокий уровень стека? Получается ли поверх этой абстракции построить широкий спектр приложений? Ну или, соответственно...
широкий спектр клиентов и насколько этот API Surface широкий. Потому что чем меньше API Surface, тем меньше вам нужно поддерживать, тем меньше вам нужно объяснять пользователям.
тем меньше у вас шансов где-то совершить ошибку, если уж совсем опростить. А можно ли попробовать приземлить это на какой-то вот совсем прикладной и простой уровень? Потому что, как Егор вначале сказал, что классно, если этот выпуск в итоге смогут прям эти советы... и взять за использовать люди, которые пишут свою библиотечку на каком-то конкретном языке, в какой-то конкретной экосистеме, которой пользуются три человека.
Друг, мама, поставлю звёздочку на гитхабе. Как будто бы там уже обычно не стоит такой широкий диапазон абстракции, и ты уже оперируешь там просто какими-то... набором классов, названием функций и так далее. Вот как можно этот совет, может быть, на какой такой простой юзкейс перенести? Конкретных советов я, к сожалению, не дам. Тут, как с врачами, приходят с вопросами, уходят с проблемами.
могу порассуждать вслух. С чем нужно определиться в случае маленьких библиотечек, это не с тем, как назвать классы и как назвать функции, а какой спектр пользовательских проблем мы хотим решать. Потому что мы же... делаем библиотеки не для того, чтобы просто у нас были библиотеки, а для того, чтобы мы решали какие-то конкретные пользовательские проблемы, абстрагировали какую-то нетривиальную функциональность.
потому что иначе пользователи бы напрямую ей воспользовались, и не было никакой бы нужды в промежуточном слое. И уже исходя из этого, строить наш API. Важно определиться, чего мы хотим достичь. Потому что если мы хотим сделать... Некоторое количество удобных хелперов над строчками — это одна история. Тогда, возможно, надо определиться с консистентным названием строковых операторов или строковых функций и просто их добавить. Если мы прячем какой-нибудь ниже стоящий слой и добавляем...
следующий. Например, мы прячем то, как компьютер хранит базу данных часовых поясов, а даем какой-нибудь красивый программный интерфейс работы с датой и временем. Это другая история. Это важно подумать, какую проблему мы решаем и что мы абстрагируем. Ты привел довольно много примеров плохих и текущих абстракций. А есть ли пример реально классной абстракции над вот тоже какой-то такой сложной нетривиальной системой, но которая все-таки задизайнена так, что она практически...
нигде не течет и прямо очень круто. Стандартная библиотека Котлина? Разумеется. Вот, ну, это на самом деле хороший ответ. Может быть, там есть прям что-то конкретное, и вещи, которыми ты гордишься, и понимаешь, что вот тут мы точно сделали прям правильно. Мне кажется, наша звезда — это библиотека KotlinX Daytime и KotlinX.io. потому что там мы нашли
очень хороший баланс между тем, что именно мы абстрагируем. Извини, ты специально выбрал библиотеки, которые еще не стабильные, которые можно просто брать и спокойно ломать обратную совместимость. Конечно, да. Но даже там мы беспокоимся об обратной совместимости.
там мы помним, что несмотря на то, что мы нестабильны, если люди на это зависят, мы, конечно, можем их сломать, но явно не одним днем, явно дав им понятный иммигрейшн-путь, явно рассказав им, почему мы это ломаем и какие есть альтернативы. но они уже без 5 минут стабильны. Окей. Тогда, может быть, есть еще какие-то советы, как вообще проектировать абстракцию, если ты находишься на том этапе, когда у тебя нет еще информации о всех кейсах использования, о всех проблемах, которые твоя библиотека...
будет решать. Ты просто пилишь какую-то классную любу для какого-то своего текущего юзкейса, но при этом ты хочешь сделать ее чуть генерализовать, попробовать, чтобы она решала потенциально не только твою проблему, но и проблему того парня. Ну, ты не знаешь, что это за тот парень и с какими проблемами он придет. Ну и, короче, да, у меня в вопросе кроется ответ, но давай. Есть общие советы и более конкретные советы. Общие советы — это посмотреть повнимательнее на домен, что раз вы пилите свой...
собственную любу, наверное, вы оперируете в этой любой с каким-то конкретным доменом. UI-элементы, даты и время, процессы, потоки, файловые системы, что-нибудь. Неплохо бы понять, какие вообще задачи люди решают с помощью этого. И, возможно, это поможет вам натренировать ваш боец. Если стараться давать более конкретные советы... то, например, с чего стоит начать, это с документации и спецификации. Из нашего опыта попытка объяснить, что какая-то вещь делает...
очень часто приводит к тому, что мы это переименовываем или вообще меняем. Потому что одно дело абстракции концепции, которыми мы оперируем в голове, а другое дело, когда вы пытаетесь объяснить это текстом или написать пример использования. Если вдруг пример не пишется или объяснить что-то крайне сложно, то, наверное, это не очень хорошая абстракция. Второй совет. Хороший способ найти нетривиальные проблемы в вашем API — это написать какую-нибудь нетривиальную программу.
Поэтому тут вы берете ваш API и в зависимости от наличия у вас. какого-то количества свободного времени, пытаетесь написать что-то другое. И вдруг оказывается, что то, к чему вы привыкли, в вашем случае не работает. Какие-то вещи не композируются, какие-то вещи не комбинируются, а какие-то, наоборот, начинают...
самым неожиданным образом друг на друга влиять. Хороший пример, как это можно найти. Есть файлы, бездонная, конечно, история API-дизайна, есть файлы. Там есть файловые локи, отличная, понятная штука. Есть hardlink. Понятная, отличная штука. Но есть некоторый класс приложений, например, база данных, которые и тем, и тем пользуются. И вот если вы взяли файловый лог на hardlink, в смысле на что-то, на что есть hardlink, а этот hardlink кто-то удалил, что должно произойти с вашим локом? Неизвестно.
Собственно, POSIX в этом месте ничего не гарантирует, там происходит что-то. Кажется, лог просто отпускается. Но такие нетривиальные программы находят очень много нетривиальных проблем. Наверное, последнее, о чем нужно думать, это требует самого большого шифта относительно прикладного или промышленного программирования. Это то, что имплементация — это, наверное, самая неважная вещь, потому что... под конкретную API-форму
В принципе, написать реализацию часто не то чтобы не очень сложно, это понятная задача. Поэтому не стоит фокусироваться на имплементации. Часто бывает, что если вы с нее начинаете, то ваши API — это просто какие-то обертки над вашей имплементацией. ничем особо от имплементации не отличаются. Про документацию пункт, это такое получается прям как documentation-driven development, только для API. Мы имплементацию пишем в test-driven development, а API у нас documentation-driven development.
Соответственно, если не получается что-то объяснить, возможно, и те, кто будут этим пользоваться, не смогут быстро это понять. На самом деле, документация... Люди не любят, когда нет документации, но когда она есть, люди ее не читают. Это абсолютно нормально.
потому что я, когда хочу решить свою прикладную задачу, не знаю, ботов в Телеграме написать, я не очень хочу читать, что там этот API делает и какие у него контракты. Если там есть метод, который называется «я вызову твой callback на каждое сообщение»,
я не буду читать, что он делает, я просто начну им пользоваться. Соответственно, если метод назван очень странно или... В его документации написано, что он делает А, но если Б, то С, а если, значит, еще вот такой флаг передать, то совсем что-то другое, то, наверное, я в этом API запутаюсь, как пользователей, буду очень недоволен. Это, собственно, еще, наверное, один практический совет, что каждая конкретная абстракция в API должна делать одну вещь хорошо.
она не должна делать много вещей. Потому что ее сразу же сложно описывать, сложно объяснить, что она делает, и сложно дать ей имя. И примеров, где это нарушается, их на самом деле достаточно много. Какой-нибудь... Я не знаю, вот в Java есть отличный класс File, который является одновременно идентификатором файла в файловой системе, еще способом его прочитать. И поэтому там, что значит два файла равны, например, не очень понятно. Собственно...
Пример того, как такой API можно эволюционировать, то, что показала Java, просто оставила эти файлы гнить и добавила новый пакет Java Neo, где уже более субъективно правильная абстракция. Это путь, собственно, идентификатор файлов. А, прикольно. То есть, когда мы говорим, что мы хотим, что все-таки API наш задизайнен фигово, но мы не хотим ломать обратную совместимость, мы просто пишем вообще абсолютно новую...
по сути, библиотеку сбоку. Да. Это, собственно, большой плюс, что вы можете сделать все с нуля и более правильно, в кавычках, потому что там тоже найдутся проблемы. Система эволюционирует, люди найдут новые способы сломать и использовать ваши API, но... с другой стороны, у вас остается вот этот burden, что существует старый API, про который есть ответы на Stack Overflow, на котором уже обучены разные языковые модели, про которые написаны книги, и он как бы с ним уже ничего не сделать.
Так, слушай, а что еще стоит держать в голове, чтобы задизайнить хороший API? Вот давай еще каких-нибудь практик, посоветуемые правила. Важно знать, как устроена экосистема вашего языка программирования или... если это не язык программирования, а какие-нибудь инструменты, плагины, например, как устроена экосистема ваших плагинов. Потому что пользователи, они любят...
Например, я тоже от них ничем не отличаюсь. Пользователи любят узнавать API и не любят его учить. Поэтому если вдруг во всей вашей экосистеме принято ошибки возвращать через Monado Result, а вы вдруг решите кидать исключение... то все окажутся недовольны. В таких случаях консистентность гораздо важнее того, что кажется, что субъективно, ну или объективно, это будет лучше.
У пользователей очень сильная мышечная память, вообще у людей очень сильная мышечная память. Стоит делать так, как сделано вокруг. Это, наверное, прям критически важный совет. Стоит подумать. И это та часть, где включается креативность нашей профессии, как гигантское количество программистов, которые очень умные, очень настойчивые и очень сильно спешат, будут решать свои проблемы вашим API.
Это как раз то место, где вылезают самые неочевидные проблемы, где люди начнут, если у них нет способа что-нибудь сделать, например... API слишком over-constrained, не дает никакой информации о ниже стоящей файловой системе или о том, какой часовой пояс используется, то они найдут способ неявно на это позависеть и все равно это сделают. Слушай, а есть ли еще какие-то советы про...
одну из самых тяжелых задач программирования — это выбор правильного названия. Потому что, опять же, кажется, вся история дизайна API — это выбор названий. Как к ним подходить? Что хорошо, что плохо? Какие подводные камни есть? Когда не получается. Не получается очень часто. Ты веселый, хотя задизайнил First Not Null of Ornull. Просто First Not Null of Ornull. Можно разговаривать очень долго.
Но на самом-то деле название должно просто отражать суть абстракции. И это важно. Почему важно не думать про имплементацию? Почему важно... писать документацию, пытаться объяснить это другим людям. Потому что мы хотим, чтобы пользователь сразу понял, чем эта штука является. Четыре колеса с трансмиссией и рулем — это, наверное, неплохое название для машины, если вы джавист.
Но если вы дизайните API, наверное, вы хотите назвать это машиной, а не то, как она устроена внутри. Соответственно, у API должно быть четкое семантическое определение, чем это является. Желательно не отступать от общепринятых практик того, как это названо уже везде и существует в индустрии. Если все называют Unicode, значит, Unicode кодпоинты кодпоинтами, то не нужно придумывать для этого новое название. Нужно назвать это кодпоинт.
В этом плане переиспользовать опыт соседних языков программирования, соседних экосистем и вообще поколений — это очень важный аспект. И тут вспоминаем операторы проверки открытости-закрытости ренджей, которые везде в каждом языке абсолютно по-разному реализованы и одинаково используются для разных вещей. Бывает и такое.
тогда можно ничего не делать и придумать что-нибудь свое. Или сказать, что мы больше похожи вот на этих, или знать, что, например, чаще на Котлине программируют из других экосистем джависты и программисты на свифте. и посмотреть, как это сделано в свифте. Важно, что недостаточно знать метод,
и просто видеть, как это сделано в другом языке. Важно понимать, почему это сделано. Почитать их дизайн документа, понять, как они его добавляли, какая у них документация, есть ли у них похожие вещи. Потому что если просто майндлесли копировать вещи из других экосистем, из других языков программирования, то получится что-то очень странно. Не могу не уточнить, что получится язык Go, но...
Я все ждал, когда мы начнем камни в ГО кидать. Время ссылок на выпуски. Про ГО, конечно же, тоже был выпуск. Блин, там мы только этим и занимались весь выпуск, что его закапывали. У меня тогда такой вопрос. Вот мы обсудили много...
классных, хороших, понятных, теоретических таких советов, да, как подойти к дизайну API, но я не могу не воспользоваться уникальной возможностью задать себе вопрос, тебе как главному за библиотеки в Котлине, а какими инструментами вот конкретно Вы в команде пользуетесь...
Опять же, для того, чтобы принимать решения по дизайну. Тем более, все новые и новые библиотеки выходят в KotlinX, возможно, как-то ваши процессы, практики тоже эволюционируют. Вот, возможно, у вас, знаешь, есть такой на доске, висит такой чек-лист.
Первое — спросить пользователей. Второе — забыть все, что ответили пользователи. Вот буквально так и написано. Вот один в один. Первое — это спросить, что хочет пользователь. Второе — это его проигнорировать. На самом деле это очень важный пункт, потому что... если спросить пользователя, что он хочет, он скажет, какое решение было бы ему удобно. Вот прямо сейчас, в моменте. Надо пытаться узнать...
что же именно хочет сделать пользователь. Если пользователь говорит, в вашей стандартной библиотеке нет функции, которая возвращает три последних символа строки. Ее, конечно, можно добавить, но лучше с этим пользователем побороться, позадавать ему наводящие вопросы.
и выяснить, что то, что он хочет, это узнать расширение файла. И добавить ему функцию на файле или на пути, которая возвращает ему это расширение. Поэтому то, с чего начинается API-дизайн библиотеки... У нас библиотеки корны, то есть те... библиотеки, поверх которых можно строить другие библиотеки и конечные приложения.
Поэтому то, с чего мы начинаем, это use case. Мы не начинаем дизайн в воздухе, мы не начинаем дизайн ради дизайна, мы пытаемся понять, какие конечные задачи будут эти пользователи решать. После этого мы начинаем обозревать решения, которые уже существуют, как... пользователи сейчас решают эту проблему в нашей экосистеме. И после этого начинаются долгие мучительные процессы API-дизайна, где кто-то про все это очень подробно думает.
пишет некоторые design proposal и design document, потому что хорошо написанный design document — это уже половина успеха. Автор, предлагая, рассказывает, какие проблемы он собирается решать, как он их собирается решать, почему и какие есть открытые вопросы. Обычно... В процессе этого вылезает огромное количество корнир-кейсов, огромное количество нестыковок, и мы начинаем пытаться их полировать и понять, не скатились ли мы в локальный максимум, и не нужно ли нам начать все сначала.
Если нам так не кажется, мы продолжаем это полировать, пока нас результат не начнет устраивать. После этого выкатываем, соответственно, API Proposal, после этого добавляем это как экспериментальный API. проверяем его сами в наших собственных приложениях, как и в продуктах JetBrains, так и передать им в наших собственных подпроектах, каких-то сэмплах, документации с тем, как это используют другие люди. И рано или поздно слушаем на фидбэк и стабилизируем.
Уточняю еще вопросик. Ты некоторое время назад говорил, что имплементация — это как раз самая неважная часть относительно всех предыдущих. Можно ли тогда сказать, что были ли кейсы, когда вы осознанно уделяли имплементации меньше времени?
или не самый оптимальный вариант, чтобы как раз побыстрее зато принять финальное решение по именно API, да, и убедиться, что окей, можем из экспериментального перевести следующий, и вот тогда-то имплементацию мы потюним и оптимизируем. Насколько это частый вообще кейс? зависит от конкретной формы API.
того, какие юзкейсы решаются. Потому что если мы, например, хотим добавить какую-нибудь новую коллекцию или новый контейнер в язык программирования, которому выдвинуты конкретные ожидания, возможно, даже неявные, что хэштаблица, наверное, не работает за квадрат, то мы, конечно... обращаем на это внимание. Если это что-то более узкоспециализированное или, например, место, от которого мы не ожидаем, что оно может стать бутлнеком в программе, хороший пример этого — это парсинг.
то мы игнорируем имплементацию. Мы сначала, соответственно, дизайним API, добавляем корректную имплементацию, которая проходит все наши тесты. проходит в требовании от спецификации, и после этого мы начинаем ее менять и начинаем ее ускорять. При этом есть обратные случаи. Например, у нас есть библиотека, которая является абстракцией над AIO, над различными контейнерами байт. Там некоторые ожидания от производительности — это требования.
к библиотеке, что если библиотека по умолчанию начинает эти байты копировать, то поправить мы это потом не сможем, потому что это уже части API. производительность ниже стоящего слоя напрямую зависит от того, какой API мы предоставим пользователям. Иногда мы даже идем по альтернативному пути, и мы пытаемся написать две имплементации, но в конечное решение идет одна.
Но мы уверены, что мы, например, можем ее заменить, если мы захотим. Как одну из практик сейчас, которой вы придерживаетесь, ты упомянул историю с выпуском экспериментальных версий библиотек. А можешь вот тоже чуть поподробнее рассказать, как вообще подходить к вот этому этапу жизни твоей библиотеки, твоего API до релиза версии 1.0? Потому что ты уже говорил такие чуть-чуть противоречащие вещи. С одной стороны, про то, что...
что это хороший момент для того, чтобы поэкспериментировать, поменять дизайн, собрать фидбэк, поправить. С другой стороны, ты же говорил, что ну вот, дейтаймы I.O. у нас все еще в Kotlin нестабильны, но мы уже стараемся обратную совместимость поддерживать. надо ли поддерживать обратную совместимость? Как вообще этот экспериментальный период проводить? Короче, расскажи.
Все зависит от, соответственно, текущего состояния библиотеки и того, как близка она к feature-completeness. Потому что, когда экспериментальная библиотека только начинается... в нее потихоньку добавляются новые кусочки. И в этот момент часто находятся новые проблемы, начинает набираться критическая масса пользователей, они начинают рассказывать, как они используют библиотеку, чего они пытаются с ее помощью сделать, чего им не хватает, или что...
например, у них получается субоптимально, и мы идем очень быстро. Мы часто ломаем API, мы добавляем много нового, мы меняем контракты, меняем реализацию, потихоньку, обрастая мясом, обрастая корным API, подбираясь к фиче Kompleteness, И в таких случаях мы, собственно, делаем то, что мы считаем нужным. Идем быстро, ломаем вещи. В какой-то момент критическая масса API...
Мы считаем, что нам уже много добавлять не нужно, что мы уже близки к feature-completeness, и осталось некоторое количество областей, например, производительность или интеракции между различными сущностями, которые осталось добавить. В этот момент...
Мы знаем, что какие-то проблемы еще найдутся. Но мы уже знаем, что из-за того, что библиотека практически feature complete, и на нее зависит... нетривиальное количество пользователей, если мы начнем ее активно и быстро ломать, это будет безответственно с нашей стороны.
Потому что мы просто создадим большому количеству людей большое количество работы. И в этот момент мы все еще и ломаем, но мы начинаем эти части агрегировать. Например, мы пытаемся все ломающие изменения провернуть через один экспериментальный релиз. Мы начинаем... рулаутить эти вещи аккуратно. Мы даем понятный миграционный путь. Потому что, если что-то существует достаточно давно, даже если оно экспериментальное, люди все равно будут на это зависеть. Потому что, смотрите, пункт один.
Кстати, миграционный путь — это отдельная интересная тема. Насколько вообще из твоей практики имеет смысл писать какие-то подробные migration-гайды с одной версии на другой, когда, опять же, возвращаясь к твоим предыдущим словам, никто... не читает документацию. На удивление, имеет смысл?
потому что никто не читает документацию. Пока они могут сделать что-то без этой документации, в случае нетривиальной миграции, документация резко начинает быть нужна, потому что непонятно, на что мигрировать. Ничего не дает понять, а что же мне делать дальше.
Мой тулинг говорит мне, вот этой вещью пользоваться нельзя. Надо пользоваться какой-то другой вещью. А эта другая вещь — это уже не одна функция, а целый набор классов. И что делать — непонятно. В этот момент Migration Guide обязательно нужен, и чем больше у вас контроля над...
и чем больше миграций вы можете предоставить, либо автоматических, либо какого-нибудь кода ассистанса, который просто дает какие-то рекомендации или на каждую сигнатуру, например, показывает, куда можно сходить и посмотреть, как смигрировать, тем лучше. Думал ли ты, может быть, уже или читал что-нибудь про то, а как вообще практики дизайна API поменяются с учетом того, что значимую часть кода пишут не люди, а генерируются LLM-ками?
Пословно говоря, те же самые миграции. Даже те места, где людям, может быть, не нужен был бы Migration Guide, если такой Migration Guide написан, и он попал в контекст или в тот датасет, на котором нейронка обучена, она за тебя...
сама пойдет и приведет ту миграцию с одной версией на другую. Короче говоря, какие implications есть у того, что парадигма разработки немного меняется сейчас? Implications следующий, что если в вашей документации есть сэмплы кода, и ваша документация соответствует какой-нибудь
понятной, может быть, не обязательно строго документированной структуре, то это значит, что это отличный датасет для того, чтобы ваши языковые модели обучать. Соответственно, чтобы она могла выдавать миграции. И более того, если у вас есть примеры кода... то это эталонный способ использования этого API. Потому что пример — это обычно что-то минимальное, простое, то, как правильно использовать.
конкретную задокументированную вещь. Это то место, где ЛЛМки должны обучаться максимально хорошо. Но на практике я пока такого не видел. Это скорее то, что может начать происходить скоро, нежели то, что происходит уже сейчас. Окей, окей, спасибо, но время покажет. Так, смотри, мы следующего вопроса уже несколько раз в течение выпуска касались, но давай попробуем сейчас...
подбить все сразу в одном месте, чтобы люди потом по таймстэмпу могли замечательно это послушать. Вот нас сейчас слушает какой-нибудь разработчик, я не знаю, на Расте, который хочет пойти и написать... дать библиотеку, там даже, не знаю, не для работы с датой и временем, не для работы с IEO, а что-нибудь такое простое, прикладное, я не знаю, обертку над Telegram API, например. Почему бы нет?
Давай попробуем дать ему каких-то таких простых, понятных, прикладных советов, которые ему точно стоят в свою работу в строе прямо завтра или послезавтра. Написать ко всему документацию. Это прям обязательно. В этот момент, скорее всего, что-нибудь найдется. убедиться, что то, что тот API, который разработчик написал, соответствует похожему API в экосистеме, потому что если в экосистеме все принято писать на коллбеках, пожалуйста, напиши все на коллбеках.
А дальше идут юзабилити эвристики, собственно, как у простых продуктов. Что если у вас все функции начинаются для Telegram-бота, например, с префикса on, то, наверное, новые функции тоже надо начинать с префикса on. Не стоит пытаться придумать новую naming-конвенцию. Нужно убедиться, что функции делают то, что написано, потому что нет ничего хуже функции remove, которая ничего не удаляет.
или функция add, которая ничего не добавляет. Нужно убедиться, что все ошибки задокументированы, и что понятно, что происходит в сценариях отказа. И убрать все ненужное. Кстати, вот ты сказал сейчас про документацию всех ошибок. И еще один вопрос здесь есть рядом. Так как люди все равно попробуют зашиться на детали того, как реализована сама библиотека под капот. надо ли всё-таки детали имплементации тоже описывать в документации.
Если это может быть важно пользователям, например, пользователям может быть важна симпатическая сложность работы с какими-нибудь контейнерами, то да, это стоит описывать в документации. Если вы... значит, пишите, я не знаю, фреймворк, который как-то хранит для ваших мобильных приложений настройки, то писать, в каком формате он хранит их. Если это нигде в API не торчит, не нужно. Окей.
И у меня еще есть ряд вопросов, которые связаны не с тем, как писать новый API, а с тем, скорее, как его монтейнить. Зачем вообще деприкейтят целые библиотеки? Условно говоря, ты написал тот же самый Ростову обертанную Telegram API. Потом в какой-то момент ты задолбался ее поддерживать. Почему просто...
Просто ее не оставить жить. Зачем люди идут и их прям официально деприкейтят, не знаю, удаляют с Mavent Central или там, где они хранятся? С Mavent Central, слава богу, ничего удалять нельзя. И поэтому, значит, наша джавэка... системы более-менее в безопасности. Причин может быть несколько. Да, но можно создать альтернативу Mavent Central, сделать ее популярной, а чтобы люди туда опубликовали в библиотеке, потом ее задеприкатить и удалить ее целиком. Привет, G-Center. Да, бывает такое.
Причин деприкейти целой библиотеки может быть несколько. Из тех, что мне приходит в голову, одна — это, например, в языке соответствующей экосистеме, например, в языке программирования, появилось что-то более прогрессивное, более удобное, эффективное, менее...
способ выражать какие-то концепции, пропагировать какие-нибудь параметры, работать с IEO, работать с синхронными вычислениями. И старый API эволюционировать невозможно, а хочется, чтобы экосистема переехала на новый, чтобы она пользовалась лучшими практиками. существующая экосистема. Это одна из причин. Другая из причин — это можно узнать, что конкретно API...
часто приводит к каким-то security-уязвимостям, как, например, некоторые системные вызовы в Linux. Или API очень error-prone, и люди в нем часто ошибаются. Просто часто бывают программные ошибки. В таком случае стоимость поддержки этого получается не сколько высокой с точки зрения самой поддержки, сколько высокой для пользователей.
что если мы знаем, что каждый, не знаю, десятый пользователей нашего API работы с файлами добавляет security-уязвимость в свое приложение, то, наверное, мы не хотим продолжать это поддерживать, мы хотим это задеприкатить и двигаться к новому API, где такого класса проблем не существует в принципе.
Мне кажется, это еще, если вы просто автор какой-то одной библиотечки, и вам в целом не так важна вся экосистема, мне кажется, это просто еще честно, знаете, по отношению к пользователю, потому что пользователь зашел и сразу же понимает, что будущего нет у этой библиотеки. нет смысла тратить время на ее затаскивание в проект. Вот, в смысле, мне кажется, это... Если нет реплейсмента, то да, можно честно написать, что библиотека unmaintained, и ее дальнейшая судьба неизвестна.
Главное не передавать ключи от публикации этой библиотеки к некоторым неизвестным мейнтейнерам, которые коммитят только в рабочие часы. Ты сейчас упомянул про одну из причин для деприкейшена — это уязвимости. А что делать с уязвимостями, если они обнаруживаются в той библиотеке, которая активно разрабатывается? Вообще, куда бежать, что обновлять, как правильно поступить? Есть такая организация, называется WASP, которая поддерживает базы данных уязвимостей.
И у них есть памятка. Что делать, если вы нашли где-то уязвимость или хотите найти? И в этой памятке несколько раз жирным написано, что все, что мы здесь пишем, не является советом. Если вы вдруг хотите что-то сделать с секьюрити-уязвимостью, проконсультируйтесь с вашим лоером. Поэтому я здесь тоже скажу, что не нужно вообще слушать...
посторонних людей в интернете, и первое, что нужно сделать, это прийти к своим безопасникам. Потому что может оказаться, что, я не знаю, вы это нашли за реверсинг инженерев. что-нибудь. А лицензия это запрещает. Или вы написали на почту мейнтейнерам и сказали, что вот есть такая security-уязвимость, если вы ее не адресуете, я, не знаю, опубликую ее через 90 дней, в соответствии с каким-нибудь популярным стандартом.
ошиблись в письме на букву, через 90 дней опубликовали, а это оказалась, не знаю, компания Oracle, и все, через пару месяцев на вас подали в суд, отсудили у вас дом, фамилию, собаку и всю вашу кодовую базу. Поэтому, если вы не знаете, что делать с вопросами security-безопасности, security-уязвимости или лицензии, найдите того, кто знает, кто получает за это деньги и кто несет за это ответственность или является accountable. Не слушайте советы в подкастах.
Блин, ты нам бизнес ломаешь сейчас. Ты понимаешь это? Извините, пожалуйста. Но там есть общие рекомендации. Обычно в больших проектах есть как раз либо баг-баунти, либо способ работы с уязвимостями. И там есть инструкция, что вот такие правила, напишите туда-то.
оцените как-нибудь импакт, пришлите репродюсер, и если мы в течение 90 дней вам не ответим, вы можете ее опубликовать. А если посмотреть с точки зрения автора библиотеки, в которой могут найти уязвимость? То есть, по сути, ты должен тогда, как автора, вот такие... какие правила как раз-таки подготовить, правильно? Да, все верно. Либо взять какие-нибудь существующие. Скорее всего, если вы пишете реально популярную библиотеку, вы...
Являетесь сотрудником какой-нибудь компании, и у нее обычно есть security policy на всю компанию. Стоит пользоваться ей. Не стоит придумывать что-то свое. Как в случае с лицензиями, так и в случае с безопасностью. И, наверное, мой заключительный вопрос. Ты сегодня, с одной стороны, много рассказывал про то, как вообще задача дизайна API проклята.
С другой стороны, ты очень красиво отвечал, что мы-то наши библиотеки делаем классно, правильно, хорошо, и все наши решения оптимальны и лучшие для наших любимых пользователей. Поэтому расскажи, пожалуйста, что-нибудь про то, чем ты... Не знаю, про самую большую твою личную ошибку, факап, неправильно принятое решение в области дизайна, о котором ты больше всего сожалеешь и сделал бы по-другому. И почему оно произошло? Я, наверное, могу рассказать.
Не то чтобы самый большой факап, но очень неприятный и немой. А потом могу рассказать про свой. Это просто разные масштабы. Про не свой. У нас в стандартной библиотеке Kotlin есть много функций, и все они следуют следующему паттерну. Например, на контейнере можно спросить первый элемент, который удовлетворяет какому-нибудь предикату. Такая функция называется first.
А если такого элемента нет, кидается исключение. А еще есть функция firstOrNull, которая возвращает null, если такого элемента не нашлось. И, соответственно, есть find и findOrNull, не знаю, reduce, reduceOrNull. Еще была функция max, которая возвращала элемент, если он соответствовал предикату, и null, если такого элемента не нашлось. Что шло в разрез буквально со всем, что существовало вокруг. Это как раз тот случай.
где произошла ошибка в консистентности именования, и вот в том самом recognition, извините, да, recognition вместо recall, И починить эту ошибку у нас заняло, кажется, три года. Через гигантский деприкейшн цикл и согласование с языковым комитетом. И где-то внутри библиотеки существует бинарная сигнатура, против которой нельзя скомпилироваться, но можно слинковаться.
от себя по-старому. И это формально breaking change для людей, которые решили с версии Kotlin 1.2 обновиться на версию Kotlin 2.0. У них вдруг код перекомпилируется по-другому. Это вот такой... Хороший пример того, насколько дорого может быть починить какую-нибудь маленькую ошибку. Из, наверное, моих больших факапов, когда мы стабилизировали Крутина, мы долго пытались решить проблему с тем, что у Крутин...
и их иерархия. Это просто конкурентное вычисление, которое как-то друг в друга вложено. Отсутствует структура. И время-то поджимало, пользователи совершали ошибки, карутины как языковая фича стабилизировались. И мы нашли идеальное решение, которое было придумано до нас, опопуляризовано чуваком, который написал асинхронную библиотеку Python. Я помню, что его зовут Нейтан, фамилию не помню. Статья называется Ghost Statement Considered Harmful. Соответственно, по аналогии с DX...
с такими go-to-statement, consider harmful, но это про go-statement, который как раз запускает асинхронное вычисление. И у него там было решение про лексические скоупы. И мы его, значит, оценили, сделали, и через два месяца нужно было стабилизироваться. И мы назвали главную сущность библиотеки по аналогии с тем, что уже существовало, контекстом. И люди...
каждый раз до сих пор в этом путаются. И даже я, когда пишу код в карутинах, регулярно спотыкаюсь в этом месте. И мне вот прям критически больно от того, что мы поспешили тогда. и застабилизировали это. Причем мы еще подумали, что сущность называется крутим-контекст, и мы подумали, что если мы назовем ее контекст, то люди будут путаться в том, что в программах много разных контекстов. Там у dependency injection фреймворков есть контексты,
у UI элементов есть контекст, у вычислений есть контекст, у базы данных. А на деле это оказалось ровно наоборот, чтобы назвали что-то... что названо по аналогии существующим, и все до сих пор об это спотыкаются. И я боюсь, что починим мы это примерно никогда, потому что это сломает вообще весь мир.
Мне нравится, как ты сейчас вот этим ответом на вопрос закольцевал выпуск и подвел нас к самому началу, когда мы вообще говорили о том, что какие-то исторические проблемы останутся с нами на века просто потому, что вот, блин, жизнь... такая и по-другому уже просто не получится. Я думаю, что это идеальный момент подвести черту выпусков. Погоди, Егор, а можно я два референса? Мне так просто хотелось, так много мы говорили о том, что правильно залог успешен.
Успеха — это сразу же выстроить ожидания, не расстраиваться, готовиться к худшему. И мне, знаешь, вот помимо референсов на выпуске про ГО, там, про какие-то ещё, про Си, мне захотелось сделать референс на выпуск про стаицизм. Почему-то такое вот было желание. Вот, возможно, тоже будет полезно. Всё. Теперь давай черту. И осознанность. Ещё у нас, кстати, был хороший выпуск.
Да, короче, пререкламировали вообще все выпуски, которые только было можно, но главное, для чего вы слушали этот выпуск, это была не реклама других, а все-таки то, о чем мы сегодня говорили, а говорили мы про дизайн API библиотек. Начали мы с замечательного исторического экскурса, вообще откуда появился термин «библиотека».
как практики дизайна библиотек развивались, откуда взялись проблемы обратной совместимости, почему они так важны, как с ними работали раньше, как работают сейчас. Поговорили о разных аспектах. того, что делает вообще задачу дизайна API сложной, что такое хороший API, на что смотреть, чтобы понять, что он хороший и почему это не очень измеримое понятие. Поговорили про различные практики, которые позволяют спроектировать ну good enough API.
И дали слушателям, кажется, довольно много классных советов про то, о чем вам стоит подумать буквально завтра, чтобы их собственная библиотека, неважно, будет ли ее использовать только их мама, бабушка и брат, или огромная толпа других. разработчиков, чтобы ими было пользоваться приятно, спокойно и надежно.
Сева, спасибо тебе большое за то, что пришел к нам в выпуск и поделился каким-то огромным багажом опыта, который ты получил и прочувствовал буквально на своей шкуре. Очень круто, классно. За байки, да. А за байки вообще отдельное спасибо. Да, это просто великолепно. Мой анекдот про сосиску я, надеюсь, в долгосрочную память переложу. Спасибо, что позвали. Было очень интересно. Так.
Катя, ты мне задашь вопрос или я тебе задам вопрос? Давай ты мне, если у тебя готова шутка на подходы. Ну, давай спрошу, что тебе нравится больше, чем сосиски? Больше этого. Мне нравятся большие кастрюльки. Субтитры сделал DimaTorzok А ещё, думаю, вставим это на видео, да, в начале выпуска на YouTube. Так вот, а больше этого, дорогие слушатели, мне нравится, когда вы приходите к нам на все соцсети, пишите ваши...
отзывы о подкасте, задаёте вопросы, ведь гости часто приходят к нам в чат и вступают в классные дискуссии. Это вообще моя любимая часть. Выпуски «Подлодки» живут ещё много лет после того, как мы их записываем. Рассказывайте про наш подкаст своим друзьям и, конечно... Самое главное — слушайте вашу любимую подлодочку. Нет, самое главное — не слушать подлодочку, а смотреть её на Ютубе, потому что Ютуб становится всё более и более важной для нас площадкой, всё больше людей, всё больше комментариев.
выпуск по России уже, не знаю, там, три сотни комментариев пробил, наверное. Поэтому, короче говоря, приходите, слушайте, но обязательно слушайте больше 30 секунд, потому что мы знаем, что алгоритмы работают так. Послушал 30 секунд, можно выключать дальше. трафик нальется. А если вы самый преданный слушатель, короче, есть просто варик стопроцентный. Но чё?
включайте выпуски, просто плейлист тех выпусков под лодки и оставляйте на ночь компьютер. Они играют, вы спите, вам не жалко, нам приятно, YouTube все считает. Мне кажется, мы впервые даем этот совет, мне нравится, я буду давать его каждый раз в новинке. Короче, вот это нам нравится больше всего. И на этом, наверное, все. Всем спасибо и всем до свидания. Хороших вам API-ев. Пока-пока. Пока-пока.