Галоўная / Артыкулы / Выбір мовы па форме проблемы: урокі з перакладу TypeScript на Go

Выбір мовы па форме проблемы: урокі з перакладу TypeScript на Go

Калі чыніцца выбор Go замест адпрацоўванага компіляра TypeScript, што можна выявіць, — гэта аднос падходоў да выбору інструментаў па навантажэнню, значэнне швальнасці інструментаў і пошаговая пераноска большых баз коду.

1223 слоў

Чаканне прыблізна ў хвіліну на автодаповнення ў великам монорепозітарыі на TypeScript — це знайомая проблема, і тое ж сама проблема відчуваюць розробніки мобільных прыстроў, калі Xcode індексуе вялікі проект. Команда TypeScript боралася самэй з гэтай проблемай, і спосаб, якіяя выбралі для ўсунення, ёсць корыстным прикладам прыйняття тэхналогічных рашэнняў на адной падставе доказаў, а не моды. У гэтым артыкуле рассматрываюцца не толькі заголовкі з теставання, але і інжынерныя аргументы, якія можна застосаваць да савых інструментоў та прыладоў, незалежна ад таго, чы вы викорыстоўваеце TypeScript, Swift чы і тое, і другое.

Што аанунсаваў Microsoft

У марце 2025 года Андерс Хейльсберг, які створыў як TypeScript, так і C#, аанунсаваў, што Microsoft стварае натыўны варіянт кампайляра TypeScript і яго мовных інструментоў. Багато хто падазрываў, што метай будзе Rust чы можа C++. Але цім быў Go.

Натычныя рэалізацыя, пазначаная кодавердзем Corsa, являецца адказоўкай для TypeScript 7.0. На момент напісання статты прыбліжны час выходу практычна паўтарыўся на середзіну 2026 года, таму перад планаваным пераходам абавязкова пераканайцеся ў актуальным статусе на офіцыяльным блогу TypeScript.

Прыбліжныя показнікі былі вельмі значныя. Час завантажэння кодавой базы VS Code ў редакторы зменіўся з прыбліжна 9,6 секунд на 1,2 секунды, викорыстоўванне памяці зменілася прыбліжна удвое, а Microsoft паўтарыла, што загальны час завантажэння проекта стаў прыбліжна у 8 разоў быстрэй, а перакантрольванне типаў — у 10 разоў быстрэй. Якщо вы хочаце дакладнаў пра тое, як гэтыя практычныя выгоды паўтарываюцца ў рэальных проектах, мы пісалі пра гэта окрема ў статты «Якія змены пракладання на Go даюць выгоды без змян у кодзе». Рэшта гэтай статты прыдзельваецца самам рашэнню.

Чаму Go, а не Rust

Команда шукала мову найнижшага рэвэлю, якая ўсё ж такі могла ствараць код у вучонай форме на кожной платформе, якую падтрымвае TypeScript, адночасна прымаючы хорашую вбудованую падтрымку канкурэнцыі. Go паспела да гэтага апісання.

Значэнне мелі ўсьмо тры фактары. Першы — навантажэння на працу: перакантрольванне типоў у большым проекте значнаю мерай ёсць праблема паралельнага перакантрольвання багато файлоў, а пасля сумавання рэзультатаў, і горутіны дужа прымусова падходзяць да гэтага патэрану. Другі — існуючы код: кампайлер ёсць вялічезная база коду JavaScript, якая спалюецца на колекцыі сметлівакоў і досыта функцыйнальны стыл. У Go таксама є колекцыя сметлівакоў і падобная структура, таму код можна было б перакласты майже без змян. Пераклад яго на Rust вымагаў бы значна глыбокай переработкі, якая б касалася прынадлежнасці і трымкі, што ўскладніла б роботу большай команды, якая мусела бы перенесці вельмі вялікую базу коду.

Простаючы, команда не выбрала наймоднейшага языка. Яна выбрала той, які падходзіў да ўсунутага ёю модэлю паралельнай роботы і дапамагаў інжынерам заставацца продуктывнымі. эта архітектурная дагадка, тая самая, яку вы прыменяеце, калі выбіраеце между UIKit і SwiftUI, або между Combine і звычным async/await для певнай функцыяльнасці.

Тая ж ідея ў Swift

Разработчыкі на Swift вялікі час сталкваліся з такой проблемай. Структураваная паралельнасць была дадана ў Swift саме для таго, каб можна было выпрацавваць ресурсаму ўтратае задачы паралельна, не жертвуючы правільнасцю. Наведзены выклік практыкуе ідею „пераглядаць многія файлы адразу, а потым з’еднаць“ за дапамогою групы задач: кожны файл атрымлея сваю сабэйную задачу, а родны элемент збірае даны пасля таго, як задачы будуць завершаны. Заўважыце, што выклік напісаны на Swift, хоць і ілюструе модэль, якую кампайлер Go выкарыстоўвае з горутінамі.

// Swift concurrency: parallel "type-checking" of files, similar in spirit
// to how Go's goroutines let TypeScript check many files at once
func checkFiles(_ paths: [String]) async -> [Diagnostic] {
    await withTaskGroup(of: [Diagnostic].self) { group in
        for path in paths {
            group.addTask {
                await checkSingleFile(path) // runs concurrently
            }
        }
        var results: [Diagnostic] = []
        for await diagnostics in group {
            results.append(contentsOf: diagnostics)
        }
        return results
    }
}

Є калькі, які варта зазначыць. Функцыя withTaskGroup гарантуе, што кожны падзейкі-дзеця завершыцца раней, чым функцыя вернуцца, таму ніякая робота не выйдзе за межы ўзору. Рэзультаты прыходзяць у порядку завершэння, а не па порядку вхідных дадзеных, таму, якщо для вас важны порядак дыягназавання, патрэбна ўпорядкуванне іх пасля. А паралелізм ёсць безпечны толькі таму, што кожная пераканальніце є незалежной, і гэта сама якосць, якая робіць пераканальніце падходячай для модэлі Go.

Якщо вы вжылі Swift 6, вы вже здаліся на такі компрэс: вы плаціце заздалегідь за строгія пераканальніце і пераканальніце безпекі, а рэзультаты прыходзяць пазней у вобразе шырэйшага стварэння коду і стабільнейшага працавання пад час адклэювання. Microsoft зробіла падобны крок зі своім компайлярам.

Урокі, які можна застосавіць у вашых сабе проектах

  • Скорасць працы інструменту ёсць атрыбутам продукту. Павільна праця і затрымкі пад автодапамогай ствараюць ўсёдневны, практычна непазірны тэкст на кожнага разработчыка ў командзе. Работа над паспективай працы інструментоў, рэдагувальных прыстрояў і систем абробкі дадзеных є настолькі ж важлівая, як і робота над паспективай самага дапрацаванага прыладу.
  • Неяк тэндэнцыі, а сама проблема павінна выбраць інструмент. Незалежна ад таго, чы вы выбіраеце MVVM, SwiftData чы Combine, праводабны адпаведны варыянт выклікаецца тым, як фактычна праходзяць дадзеныя через вашу систему, а не тым, што є популярным у сяброўскіх меражах за гэты месц.
  • Перапісвы можаць быць поэтапнымі. Команда, якая працавала над TypeScript, перенесла існуючы кампайляр як магчыма аддаўнальна, замест таго, каб перадзяліць яго, таму новая версія дае тыя ж рэзультаты, чым старая. Гэта ўпэўнены аргумент праць проты перабудовы цэлай архітектуры прыладу за адзін спринт.

Калі не трэба следаваць гэтам прыкладу? Якщо ваша ныячая структура не ўтварае вузькага месца, пераключэнне на іншую тэхналогію не прыносіць нічога, а толькі рызыкуе. Спачатку здзейсніце замеры і пераканаўцеся, што самэ ўповільненне дзейсна выклікаецца тым слоем, які вы плануеце заменіць.

Адрасаванне сігналаў апазоры

Сімптамы, якія спрычынілі гэты проект, часта з’яўляюцца ў будзь-якай велікай базе коду: уповільненне процеса адгадвання типаў, затрымкі у паведамленнях редактара і постаўнэ расшырэнне структуры процеса складання. Рашэннем не завжды ёст мяркавае фрэймворк; інодзе трэба рэштыцьваўна паглядзець, што на самай працэ вашы інструменты фактычна робяць, і зменіць ту частку, якая выканае занадта многа роботы.

Ключовыя выводы

  • Go быў выбраны таму, што яго власны процес компіляцыі, сборчыка мусора і паралельнае выкананне на адной гарутайн падходзіць як да задач з перакантролювання типаў, так і да структуры існуючага коду.
  • Ускорэння былі адзначаны завяжана з аднацоўным перакладам, які падтрымваў аднаковае прыменшэнне пад час зменыя апаратнага сераўеру.
  • Спрыймайце затрэмтванне інструментаў для разработчыка як рэальную вартасць, зарады якой варта дакласті зусілляў у інжынерным аспекте.
  • Калі наступны раз процес стварэння продукту будзе працаваць медленна, задайце тую ж запытанне, якое ставіла команда TypeScript: який інструмент на самай працоўна падходзіць для рашэння гэтай проблемы?
  • Спаднія статты