Погляд на технології останнього десятиліття

Прим. перев.: Ця стаття, що стала хітом на Medium, - огляд ключових (за 2010-2019 роки) змін у світі мов програмування та пов'язаної з ними екосистеми технологій (особлива увага приділяється Docker та Kubernetes). Її оригінальним автором є Cindy Sridharan, яка спеціалізується на інструментах для розробників та розподілених систем — зокрема, вона написала книгу «Distributed Systems Observability» — і досить популярна в інтернет-просторі серед IT-фахівців, які особливо цікавляться темою cloud native.

Погляд на технології останнього десятиліття

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

Хочу відразу обмовитися, що в цій статті я не охоплюю зміни в таких галузях, як наука про дані (data science), Штучний інтелект, frontend engineering і т.п., оскільки особисто у мене відсутній достатній досвід у них.

Типізація завдає удару у відповідь

Однією з найпозитивніших тенденцій 2010-х стало відродження мов із статичною типізацією. Втім, подібні мови нікуди і не зникали (С++ і Java затребувані сьогодні; домінували вони і десять років тому), проте мови з динамічною типізацією (динамічні) пережили значне зростання популярності після руху Ruby on Rails в 2005 році. Це зростання досягло максимуму в 2009 році з відкриттям вихідного коду Node.js, завдяки якому Javascript-на-сервері став реальністю.

Згодом динамічні мови втратили частину своєї привабливості у сфері створення серверного ПЗ. Мова Go, популяризована в ході контейнерної революції, здавалася краще пристосованою до створення високопродуктивних, ресурсоефективних серверів з паралельною обробкою інформації (з чим згоден сам автор Node.js).

Rust, представлений у 2010-му, включив у себе досягнення у теорії типів у спробі стати безпечною та типізованою мовою. У першій половині десятиліття ставлення до Rust у галузі було досить прохолодним, проте у другій половині його популярність значно зросла. Серед примітних прикладів використання Rust можна назвати його застосування для Magic Pocket у Dropbox, Firecracker від AWS (Ми розповідали про нього в цієї статті - прим. перев.), достроковому WebAssembly-компіляторі Lucet від Fastly (нині входить у bytecodealliance) та ін. В умовах, коли Microsoft розглядає можливість переписати деякі частини ОС Windows на Rust, можна з упевненістю сказати, що в 2020-х ця мова чекає на світле майбутнє.

Навіть динамічні мови отримали нові можливості на зразок опціональних типів (optional types). Вперше вони були реалізовані в TypeScript - мові, що дозволяє створювати типізований код і компілювати його JavaScript. PHP, Ruby та Python обзавелися власними системами опціональної типізації (міпі, мотика), які успішно використовуються в виробництво.

Повернення SQL до NoSQL

NoSQL - ще одна технологія, яка на початку десятиліття була набагато популярнішою, ніж наприкінці. Думаю, на це є дві причини.

По-перше, модель NoSQL з відсутністю схеми, транзакцій і слабшими гарантіями узгодженості виявилася складнішою в реалізації, ніж модель SQL. У блог-нотатці з назвою «Чому слід віддавати перевагу суворій узгодженості за будь-якого зручного випадку» (Why you should pick strong consistency, whenever posible) Google пише:

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

Друга причина пов'язана зі зростанням «масштабованих» розподілених баз даних SQL (таких як Хмарний ключ и AWS Аврора) у публічному хмарному просторі, а також Open Source-альтернатив на кшталт CockroachDB (про неї ми теж писали - прим. перев.), які вирішують багато технічних проблем, через які традиційні SQL-бази «не масштабувалися». Навіть MongoDB, яка колись була уособленням руху NoSQL, тепер пропонує розподілені транзакції.

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

Тотальна стріміфікація

Apache Kafka, без сумніву, став одним із найважливіших винаходів минулого десятиліття. Його вихідний код було відкрито у січні 2011-го, і за ці роки Kafka зробив справжню революцію у роботі бізнесу з даними. Kafka використовувався у всіх компаніях, у яких мені довелося працювати, починаючи від стартапів і закінчуючи великими корпораціями. Гарантії та варіанти використання (pub-sub, потоки, подієво-орієнтовані архітектури), що їм надаються, застосовуються в різних завданнях: від організації зберігання даних до моніторингу та потокової аналітики, — затребуваних у багатьох галузях, таких як фінанси, охорона здоров'я, держсектор, роздрібна торгівля та і т.д.

Безперервна інтеграція (і меншою мірою безперервне розгортання)

Безперервна інтеграція (Continuous Integration) з'явилася не останні 10 років, проте саме за минулу декаду вона поширилася настількищо стала частиною стандартного робочого процесу (run-тести на всіх pull request'ах). Становлення GitHub як платформа з розробки та зберігання коду і, що важливіше, розвиток робочого процесу на основі GitHub flow означає, що проведення тестів до прийняття pull request'у в майстер — це єдиний workflow у розробці, знайомий інженерам, які розпочали свої кар'єри в останні десять років.

Безперервне розгортання (Continuous Deployment; розгортання кожного комміту у тому вигляді й у той час, що він потрапляє у майстер) негаразд широко поширене, як безперервна інтеграція. Втім, з безліччю різних хмарних API для розгортання, зростаючою популярністю платформ на кшталт Kubernetes (що надають стандартизований API для розгортань) і поява мульти-платформних, мульти-хмарних інструментів на кшталт Spinnaker (побудованих поверх згаданих стандартизованих API), процеси розгортання , загалом, безпечнішими.

контейнери

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

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

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

Без сервера

Готова посперечатися, що поява «безсерверних» обчислень навіть важливіша за контейнери, оскільки вона дійсно дозволяє втілити в реальність мрію про обчислення на запит (На вимогу). В останні п'ять років я спостерігала за поступовим розширенням сфери застосування безсерверного підходу (додавалася підтримка нових мов та середовищ виконання). Виникнення таких продуктів, як Azure Durable Functions, є вірним кроком на шляху реалізації stateful-функцій (принагідно вирішальним деякі проблемипов'язані з обмеженнями FaaS). З цікавістю поспостерігаю, як ця нова парадигма розвиватиметься найближчими роками.

Автоматизація

Мабуть, найбільше від цього тренду виграло співтовариство інженерів з експлуатації, оскільки саме він дозволив втілити у життя концепції на кшталт «інфраструктура як код» (IaC). Крім того, пристрасть до автоматизації співпала зі зростанням «культури SRE», метою якої є більш програмно-орієнтований підхід до експлуатації.

Загальна API-фікація

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

Крім того, API-фікація – це перший крок до SaaS-фікації деякого функціоналу чи інструменту. Ця тенденція також збіглася зі зростанням популярності мікросервісів: SaaS став просто ще одним сервісом, з яким можна працювати за API. В даний час є безліч SaaS-і FOSS-інструментів в таких областях, як моніторинг, платежі, балансування навантаження, безперервна інтеграція, оповіщення, перемикання функціональності (Feature flagging), CDN, інжиніринг трафіку (наприклад DNS) і т.д., які процвітали в минулому десятилітті.

Спостережуваність

Варто зазначити, що сьогодні нам доступні набагато більш просунуті інструменти для моніторингу та діагностики поведінки додатків, ніж будь-коли раніше. Систему моніторингу Prometheus, яка отримала статус Open Source у 2015 році, можна назвати, мабуть, найкращою системою моніторингу із тих, з якими мені доводилося працювати. Вона не досконала, проте значна кількість речей у ній реалізована цілком правильним чином (наприклад, підтримка вимірювань [dimensionality] у разі метрик).

Розподілене трасування стало ще однією технологією, що вийшла в мейнстрім у 2010-х завдяки таким ініціативам, як OpenTracing (і її наступниці OpenTelemetry). Хоча трасування, як і раніше, досить складне у застосуванні, деякі з останніх розробок дозволяють сподіватися, що в 2020-х ми розкриємо її справжній потенціал. (Прим. перекл.: Читайте також у нашому блозі переклад статті «Розподілене трасування: ми все робили не так» цього ж автора.)

Заглядаючи в майбутнє

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

Вирішення проблеми закону Мура

Кінець закону масштабування Деннарда та відставання від закону Мура вимагають нових інновацій. John Hennessy в своєї лекції пояснює, чому проблемно-залежні (domain specific) архітектури на зразок TPU можуть стати одним із рішень проблеми відставання від закону Мура. Тулкіти начебто MLIR від Google вже видаються хорошим кроком уперед у цьому напрямку:

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

CI/CD

Хоча зростання популярності CI стало одним із головних трендів 2010-х, Jenkins, як і раніше, залишається золотим стандартом CI.

Погляд на технології останнього десятиліття

Цей простір гостро потребує інновацій у таких областях:

  • інтерфейс користувача (DSL для кодування тестових специфікацій);
  • деталі реалізації, які зроблять його по-справжньому масштабованим та швидким;
  • інтеграція з різними середовищами (staging, prod тощо) для здійснення більш просунутих форм тестування;
  • безперервна перевірка та розгортання.

Інструменти розробників

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

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

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

Також було б цікаво розвинути концепцію «перенесених середовищ» на інші галузі розробки, такі як відтворення помилок (або flaky tests), що зустрічаються в певних умовах або за певних налаштувань.

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

Обчислення (майбутнє PaaS)

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

Погляд на технології останнього десятиліття

У зв'язку з цим виникає кілька цікавих питань. Насамперед, список доступних варіантів у публічній хмарі постійно зростає. Постачальники хмарних послуг мають персонал і ресурси, що дозволяють їм з легкістю йти в ногу з останніми досягненнями у світі Open Source і випускати продукти на кшталт «serverless pod'ів» (підозрюю, просто роблячи їх власні FaaS runtime'и сумісними з OCI) або інші схожі химерні штуки.

Тим, хто користується цими хмарними рішеннями, можна лише позаздрити. Теоретично хмарні пропозиції Kubernetes (GKE, EKS, EKS на Fargate і т.д.) надають незалежні від хмарного провайдера API для запуску робочих навантажень. Якщо ви користуєтеся подібними продуктами (ECS, Fargate, Google Cloud Run та ін.), то, ймовірно, вже максимально задієте найцікавіші функції, пропоновані постачальником послуг. Крім того, з появою нових продуктів або обчислювальних парадигм міграція, швидше за все, буде простою та безтурботною.

Враховуючи те, як швидко розвивається спектр подібних рішень (я дуже здивуюся, якщо найближчим часом не з'явиться пара-трійка нових варіантів), невеликим «платформним» командам (командам, пов'язаним з інфраструктурою та відповідальним за створення платформ on-premise для запуску робочих навантажень) в компаніях) буде неймовірно важко конкурувати у плані функціональних можливостей, простоти використання та загальної надійності. 2010-і пройшли під знаком Kubernetes як інструменту для створення PaaS (платформа-як-послуга), тому мені здається абсолютно безглуздим створення внутрішньої платформи на базі Kubernetes, що пропонує ті самі можливості вибору, простоту та свободу, доступні в публічному просторі хмар. Уявлення про засновану на «контейнерах» PaaS як про «стратегію Kubernetes» рівнозначне умисній відмові від використання найінноваційних можливостей хмари.

Якщо подивитися на доступні сьогодні Обчислювальні можливості, стає очевидним, що створення власної PaaS виключно на базі Kubernetes рівносильне тому, щоб власноруч загнати себе в кут (не дуже далекоглядний підхід, так?). Навіть якщо хтось вирішить сьогодні створити контейнерну PaaS на базі Kubernetes, через пару років вона виглядатиме застарілою, порівняно з хмарними можливостями. Хоча Kubernetes розпочинав своє існування як проект з відкритим вихідним кодом, його прабатьком та ідейним натхненником виступає відповідний внутрішній інструмент Google. Однак він спочатку розроблявся на початку/середині 2000-х, коли обчислювальний ландшафт був зовсім іншим.

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

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

Все це підводить мене до наступного висновку: для роботи нам потрібні кращі, більш високорівневі абстракції (особливо це справедливо для абстракцій найвищого рівня).

Правильний API найвищого рівня

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

Проблема Docker'а в тому, що (принаймні) перед проектом були поставлені надто глобальні цілі: все заради вирішення проблеми сумісності («працює на моїй машині») за допомогою контейнерної технології. Docker був і форматом образів, і runtime'ом із власною віртуальною мережею, і інструментом CLI, і демоном, що працює під root'ом, та багатьом іншим. У всякому разі, обмін повідомленнями був більш заплутаним, не кажучи вже про «легковажні VM», контрольні групи, простори імен, численні проблеми з безпекою та функції впереміш з маркетинговим закликом «створювати, постачати, запускати будь-який додаток де завгодно».

Погляд на технології останнього десятиліття

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

Kubernetes багато в чому поділяє ті ж проблеми, що і Docker. Незважаючи на всі розмови про круту і складну (composable) абстракцію, поділ різних завдань на шари не надто добре інкапсульовано. У своїй основі це оркестратор контейнерів, який запускає контейнери у кластері, що складається з різних машин. Це досить низькорівневе завдання, яке застосовується тільки до інженерів, що експлуатують кластер. З іншого боку, Kubernetes це також абстракція найвищого рівня, CLI інструмент, з яким користувачі взаємодіють через YAML.

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

Dockerfile та CLI-утиліта docker повинні стати прикладом побудови гарного «інтерфейсу користувача найвищого рівня». Рядовий розробник може приступити до роботи з Docker'ом, нічого не знаючи про тонкощі реалізації, що роблять внесок в експлуатаційний досвід, таких як простори імен, контрольні групи, обмеження пам'яті та CPU і т.д. Зрештою написання Dockerfile'а не дуже відрізняється від написання shell-сценарію.

Kubernetes призначений для різних цільових груп:

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

Підхід «один API підходить для всього», застосований у Kubernetes, є недостатньо інкапсульованою «горою складності» без вказівки того, як її масштабувати. Все це призводить до невиправдано затяжної траєкторії навчання. Як пише Adam Jacob, «Docker приніс користувачам трансформуючий досвід, який досі не перевершили. Запитайте у будь-кого, хто використовує K8s, чи хотіли б вони, щоб він працював як їх перший docker run. Відповідь буде ствердною»:

Погляд на технології останнього десятиліття

Я б сказала, що основна частина інфраструктурної технології сьогодні надто низькорівнева (і, отже, вважається «надто складною»). Kubernetes реалізовано на досить низькому рівні. Розподілене трасування в її нинішній формі (Більшість span'ів, пошитих разом, щоб сформувати traceview) також реалізована на занадто низькому рівні. Інструменти для розробників, що реалізують «абстракції найвищого рівня», як правило, виявляються найуспішнішими. Цей висновок виявляється справедливим у дивовижній кількості випадків (якщо технологія надто комплексна або складна у використанні, то «API/UI найвищого рівня» для цієї технології ще тільки належить відкрити).

Прямо зараз екосистема cloud native бентежить своєю зацикленістю на низькорівневості. Як галузь ми маємо впроваджувати інновації, експериментувати та навчати того, як виглядає правильний рівень «максимальної, найвищої абстракції».

Роздріб

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

Хоча я не маю конкретних думок щодо розвитку цієї галузі в наступному десятилітті, я буду сильно розчарована, якщо 2030-го ми будемо робити покупки так само, як робимо це 2020-го.

Журналістика

Я все більше розчаровуюсь у стані світової журналістики. Стає все важче знаходити неупереджені ресурси новин, які мовлять об'єктивно і педантично. Дуже часто кордон між самою новиною та думкою про неї стирається. Як правило, інформація подається упереджено. Це особливо справедливо у випадку деяких країн, де історично не існувало поділу між новиною та думкою про неї. У недавній статті, опублікованій після останніх загальних виборів у Великій Британії, Alan Rusbridger, колишній редактор The Guardian, пише:

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

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

Соціальні мережі

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

Соціальні мережі також є найпотужнішим медійним засобом з усіх, що будь-коли існували. Вони кардинально змінили політичну практику. Вони змінили рекламу. Вони змінили поп-культуру (наприклад, основний внесок у розвиток т.зв. cancel culture [культури остракізму - прим. перев.] вносять саме соціальні мережі). Критики стверджують, що соціальні мережі виявилися родючим ґрунтом для швидких і «примхливих» змін у моральних цінностях, проте вони також забезпечили представникам маргінальних груп можливість об'єднуватися (раніше вони ніколи не мали такої можливості). По суті, соціальні мережі змінили спосіб спілкування та спосіб самовираження людей у ​​XXI столітті.

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

Я запитую, чи можна створити «найкращу» платформу, яка сприятиме підвищенню якості дискусій? Адже саме те, що рухає залученням, часто і приносить основний прибуток цим платформам. Як пише Kara Swisher у New York Times:

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

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

PS від перекладача

Читайте також у нашому блозі:

Джерело: habr.com

Додати коментар або відгук