Tässä on totuus, jonka täyden arvostamisen oppimiseen meni vuosia: koodisi ulkonäöllä on melkein yhtä suuri merkitys kuin sillä, mitä se tekee. Hyvin muotoiltu koodi katselmoituu nopeammin, sisältää vähemmän bugeja ja on dramaattisesti helpompi ylläpitää. Ja toisaalta oikein minifioitu koodi latautuu nopeammin ja säästää käyttäjien kaistanleveyttä.

Puhutaan tämän kolikon molemmista puolista.

Miksi muotoilu on tärkeämpää kuin luulet

Olen nähnyt tiimien tuhlaavan tunteja koodikatselmoinneissa väitellessään tabulaattoreista vs välilyönneistä, puolipisteistä vai ei, tai aaltosulkujen sijainnista. Se on aikaa, jota ei käytetä ominaisuuksien toimittamiseen. Johdonmukainen muotoilu eliminoi nämä väittelyt kokonaan.

Mutta kyse ei ole vain tiimihengestä. Yhtenäisesti muotoiltu koodi tekee bugeista helpompia havaita. Mieti tätä:

javascript

Tuo sendNotification()-kutsu suoritetaan joka kerta, ei vain ylläpitäjille — sisennys on harhaanjohtava. Pakollisilla aaltosulkeilla (joita useimmat muotoilijat vaativat) tämä bugi on ilmeinen:

javascript

Muotoilun parhaat käytännöt

Valitse tyyli ja automatisoi se. Älä luota ihmisiin johdonmukaisessa muotoilussa. Käytä Prettieriä — sillä on vahvat mielipiteet, ja juuri se on pointti. Aseta se suoritettavaksi tallennuksen yhteydessä, etkä enää koskaan mieti muotoilua.

Sisennys: 2 välilyöntiä on JavaScript/TypeScript-konventio. Googlen tyyliopas, Airbnbin tyyliopas ja Standard ovat kaikki samaa mieltä tästä.

Puolipisteet: Käytä niitä. Kyllä, JavaScriptissä on ASI (Automatic Semicolon Insertion), mutta sillä on hyvin dokumentoituja sudenkuoppia. Laita vain ne puolipisteet.

Rivin pituus: Pidä rivit alle 80-100 merkin. Pitkät rivit aiheuttavat vaakasuuntaista vieritystä ja tekevät diffeistä vaikeampia lukea.

Miten minifiointi todella toimii

Minifiointi on muotoilun vastakohta — se tekee koodista mahdollisimman pientä tuotantoa varten. Tässä on mitä minifioija kuten Terser tekee:

  • Poistaa välilyönnit ja kommentit — Kaikki ne välilyönnit, tabulaattorit, rivinvaihdot ja // TODO: fix later -kommentit? Poissa.
  • Lyhentää muuttujanimetuserAccountBalance muuttuu a:ksi. calculateMonthlyPayment muuttuu b:ksi. Vain paikalliset muuttujat — se ei nimeä uudelleen asioita, joihin ulkoinen koodi saattaa viitata.
  • Poistaa kuolleen koodin — Jos funktiota ei koskaan kutsuta, se poistetaan.
  • Esikäsittelee vakiotconst TAX_RATE = 0.2; total * (1 + TAX_RATE) muuttuu total*1.2:ksi.

Tulos? Tyypillisesti 50-70% tiedostokoon pienennys. Tässä ennen/jälkeen:

Ennen (luettava, 147 tavua):

javascript

Jälkeen (minifioitu, 62 tavua):

javascript

Sama toiminnallisuus, 58% pienempi.

Älä unohda Source Mappeja

Minifioitu koodi on lukukelvotonta, mikä tekee tuotantovirheiden debuggaamisesta tuskallista. Source mapit ratkaisevat tämän yhdistämällä minifioidun koodin alkuperäiseen lähdekoodiisi. Jokainen moderni build-työkalu generoi ne — varmista, että lataat ne virheenseurantapalveluusi (Sentry, Bugsnag, jne.).

Käytännön työnkulku

Käytännön työnkulku

Kehityksessä: kirjoita muotoiltua, luettavaa koodia. Käytä Prettieriä + ESLintiä automaattiseen muotoiluun ja lintaukseen. Tuotannossa: minifioi kaikki build-työkalullasi (webpack, Vite, esbuild).

Prettierin + ESLintin asennus 60 sekunnissa

Jos et ole vielä automatisoinut muotoilua, tässä on nopein asennus:

javascript

Lisää sitten muotoiluskripti package.json-tiedostoosi: "format": "prettier --write src/**/*.{js,ts,jsx,tsx}". Aja se kerran muotoillaksesi koko koodikantasi ja aseta editorisi muotoilemaan tallennuksen yhteydessä. Ensimmäinen diffi voi olla valtava, mutta sen jälkeen muotoiluväittelyt ovat ohi ikuisesti.

Tree Shaking vs minifiointi

Ihmiset sekoittavat nämä kaksi usein, mutta ne ovat erilaisia optimointitekniikoita, jotka toimivat yhdessä:

  • Minifiointi tekee yksittäisistä tiedostoista pienempiä poistamalla välilyöntejä, lyhentämällä nimiä ja esikäsittelemällä lausekkeita. Se ei poista käyttämättömiä exportteja.
  • Tree shaking poistaa käyttämätöntä koodia moduulien välillä. Jos tuot vain { debounce } lodash-es:stä, tree shaking poistaa kaiken muun bundlesta.

Molemmat ovat välttämättömiä tuotantobuildeille. Modernit bundlerit kuten Vite ja esbuild tekevät molemmat automaattisesti — varmista vain, että käytät ES-moduuli-importteja (import) CommonJS:n (require) sijaan, jotta tree shaking voi analysoida riippuvuusgraafisi.

Yleiset minifioinnin sudenkuopat

1. function.name:een luottaminen tuotannossa. Minifioijat nimeävät funktiot uudelleen, joten myFunction.name palauttaa "a" tai jotain yhtä hyödytöntä tuotannossa. Jos tarvitset vakaita funktionnimiä (lokitusta, virheenseurantaa tai reflektiota varten), käytä sen sijaan eksplisiittisiä merkkijonotunnisteita.

2. Koodin minifiointi, joka käyttää eval():ia. eval() voi viitata mihin tahansa muuttujaan nimellä, joten minifioija ei voi turvallisesti nimetä mitään uudelleen siinä scopessa. Useimmat minifioijat ohittavat uudelleennimeämisen funktioissa, jotka sisältävät eval():n, mutta tämä rajoittaa optimointia merkittävästi. Vältä eval():ia kokonaan, jos mahdollista.

3. Minifioitujen buildien testaamattomuus. Jotkut bugit ilmenevät vasta minifioinnin jälkeen — erityisesti property-nimien manglauksen yhteydessä. Aja testisuittisi aina tuotantobuildia vasten, ei pelkästään kehitysbuildia.

Bundlekoon mittaaminen

Minifioinnilla on merkitystä vain, jos tiedät mistä lähdet liikkeelle. Tässä nopeita tapoja tarkistaa bundlekokosi:

javascript

Nämä työkalut generoivat visuaalisia puukarttoja, jotka näyttävät tarkalleen mitkä riippuvuudet syövät bundleasi. Saatat yllättyä — löysin kerran projektin, jossa moment.js:n lokaalit muodostivat 500KB lopullisesta bundlesta. Vaihtaminen dayjs:ään säästi 490KB välittömästi.

Prettier vs ESLint: Ne eivät ole sama asia

Okei, minun täytyy sanoa tämä, koska näen tätä sekaannusta *kaikkialla*: Prettier ja ESLint eivät ole sama työkalu. Ne eivät edes tee samaa työtä. Ja silti näen jatkuvasti kehittäjiä, jotka kohtelevat niitä vaihtokelpoisina. Anna kun selitän.

Prettier on koodin muotoilija. Se välittää siitä, miltä koodisi *näyttää* — välilyönnit, rivinvaihdot, puolipisteet, loppupilkut, lainausmerkkityyli, sisennys. Siinä kaikki. Se ei tiedä eikä välitä toimiiko koodisi oikeasti. Sinulla voisi olla funktio, joka poistaa koko tietokantasi, ja Prettier vain varmistaisi, että se on kauniisti sisennetty.

ESLint toisaalta on linteri. Se välittää koodin *laadusta* — käyttämättömät muuttujat, saavuttamaton koodi, mahdolliset bugit, puuttuva virheenkäsittely, saavutettavuusongelmat. Se nappaa ne asiat, jotka puraisevan sinua lauantaina klo 2 yöllä, kun sinut herätetään hälytyksellä.

Mutta tässä on se juttu — ESLintissä on *myös* joitain sisäänrakennettuja muotoilusääntöjä, ja siinä kohtaa asiat menevät sotkuisiksi. Jos ajat molemmat työkalut ilman oikeaa konfiguraatiota, ne tappelevat keskenään. Prettier muotoilee koodisi yhdellä tavalla, ESLint valittaa että sen pitäisi olla toisella tavalla, korjaat sen ESLintille, Prettier muotoilee uudelleen... se on loputon turhautumisen kierre.

Ratkaisu on yksinkertainen: käytä eslint-config-prettier:iä. Se sammuttaa kaikki ESLint-säännöt, jotka ovat ristiriidassa Prettierin kanssa, jotta ESLint keskittyy koodin laatuun ja Prettier hoitaa muotoilun. Ei enää tappeluja.

javascript

Huomaa, miten "prettier" on viimeinen kohta extends-taulukossa? Se on ratkaisevaa — sen täytyy yliajaa muotoilusäännöt yläpuolella listatuista plugineista. Olen nähnyt tiimien käyttävän tunteja ESLint/Prettier-ristiriitojen debuggaamiseen vain huomatakseen, että järjestys oli väärä. Luota minuun tässä.

Suositukseni: anna Prettierin hoitaa kaikki kosmeettinen, ja konfiguroi ESLint-säännöt, jotka oikeasti nappaavat bugeja. Älä tuhlaa ESLintiä valittamaan puolipisteistä, kun Prettier hoitaa sen jo.

Suuri tabulaattorit vs välilyönnit -väittely (ratkaistu)

No niin, puhutaan ohjelmoinnin pyhästä sodasta. Tabulaattorit vai välilyönnit? Olen nähnyt ystävyyksien päättyvän tämän väittelyn takia. Olen nähnyt Slack-ketjujen jatkuvan *päiväkausia*. Henkilökohtaisesti todistin, kun senior-kehittäjä kirjoitti 2 000 sanan Confluence-sivun puolustaakseen tabulaattoreita. Se oli mahtavaa ja täysin tarpeetonta.

Katsotaan todellisia lukuja. Stack Overflow Developer Survey on johdonmukaisesti osoittanut, että välilyönnit ovat suositumpia — noin 60-65% kehittäjistä suosii välilyöntejä. GitHubin oma analyysi julkisista repoista kertoo samanlaista tarinaa.

Mutta tässä on kiinnostavaa: se vaihtelee hurjasti kielen mukaan. Go käyttää tabulaattoreita — se ei ole edes keskustelu, gofmt pakottaa tabulaattorit eikä kukaan väitä gofmt:n kanssa. Python käyttää 4 välilyöntiä — PEP 8 sanoo niin, eikä PEP 8:n kanssa väitellä myöskään. JavaScript ja TypeScript? Yhteisö on pitkälti asettunut 2 välilyönnin kannalle, ja käytännössä kaikki merkittävät tyylioppaat ovat samaa mieltä.

Saavutettavuusargumentti tabulaattoreiden puolesta on tosin oikeasti vakuuttava — tabulaattorit antavat jokaiselle kehittäjälle mahdollisuuden asettaa oman visuaalisen leveytensä, mikä on tärkeää näkövammaisille kehittäjille. Se on todellinen, perusteltu syy suosia tabulaattoreita.

Mutta tiedätkö mitä? Tässä on *oikeasti* oikea vastaus, ja tarkoitan tätä vilpittömästi: käytä mitä tahansa muotoilijasi pakottaa ja lopeta väittely. Jos projektisi käyttää Prettieriä tabWidth: 2:lla ja useTabs: false:lla, käytät 2 välilyöntiä. Piste. Muotoilija tekee päätöksen, sinä hyväksyt sen ja käytät energiasi asioihin, joilla on oikeasti väliä — kuten siihen, kaatuuko sovelluksesi, kun joku kirjoittaa emojin hakukenttään.

Elämä on liian lyhyt muotoiluväittelyihin. Anna robottien päättää.

Moderni minifiointi: esbuild, SWC ja nopeusrevoluutio

Vuosien ajan Terser oli JavaScript-minifioinnin kuningas. Se korvasi UglifyJS:n, se oli taistelutestettu, se toimi hienosti. Ainoa ongelma? Se on hidas. Todella *oikeasti* hidas suurilla koodikannoilla. Enkä tarkoita "hae kahvia" -hidasta — tarkoitan "harkitse uravalintojasi uudelleen tuijottaessasi CI-putkea" -hidasta.

Sitten esbuild ilmestyi ja muutti kaiken. Go-kielellä kirjoitettu esbuild on 10-100x nopeampi kuin Terser. En liioittele — benchmark-tulokset ovat lähes koomisia. Projekti, jonka minifiointi vie Terseriltä 30 sekuntia? esbuild tekee sen 300 millisekunnissa. Ensimmäisen kerran kun näin sen, luulin aidosti että jokin on rikki, koska se valmistui niin nopeasti.

SWC on toinen kilpailija, kirjoitettu Rustilla. Se ei ole aivan yhtä nopea kuin esbuild puhtaaseen minifiointiin, mutta se on kattavampi työkaluketju — se hoitaa transpilauksen, bundlauksen ja minifioinnin yhdessä paketissa. Jos käytät Next.js:ää, käytät jo SWC:tä konepellin alla.

Tässä karkea vertailu keskikokoisella projektilla (~500 JS-tiedostoa):

ToolLanguageMinification TimeNotes
TerserJavaScript~25sBattle-tested, most compatible
esbuildGo~0.3sBlazing fast, some edge cases
SWCRust~0.8sFull toolchain, great ecosystem

Onko nopeusero oikeasti merkityksellinen? Rehellisesti, pienelle projektille 5 sekunnin buildilla, luultavasti ei. Mutta kun ajat CI/CD:tä suurella monorepolla ja build-putkesi pyörii 50 kertaa päivässä? Ne minuutit kasautuvat nopeasti. Olen nähnyt tiimien lyhentävän CI-aikojaan 10+ minuutilla pelkästään vaihtamalla Terseristä esbuildiin.

Hyvä uutinen on, että jos käytät Viteä, saat jo esbuildin kehitysbuildeille ja Rollupin (Terserin tai esbuildin kanssa) tuotantoon. Next.js käyttää SWC:tä. Angular on myös kokeillut esbuildia. Ekosysteemi liikkuu nopeasti täällä.

Yksi varoituksen sana: esbuild ja SWC eivät aina tuota tavu-tavulta identtistä tulosta Terserin kanssa. Harvinaisissa tapauksissa Terserin aggressiivisemmat optimoinnit tuottavat hieman pienempiä bundleja. Mutta puhumme ehkä 1-2% erosta — useimmissa tapauksissa ehdottomasti 100x nopeusparannuksen arvoista.

CSS- ja HTML-minifiointi myös

Olemme puhuneet JavaScriptistä, mutta älä unohda CSS- ja HTML-minifiointia. Vakavasti, olen nähnyt projekteja, joissa CSS-bundle oli *suurempi* kuin JavaScript. Varsinkin jos käytät utility-first-kehystä kuten Tailwindia (ennen kuin PurgeCSS tekee tehtävänsä).

CSS:lle cssnano on vakiotyökalu. Se tekee enemmän kuin pelkkä välilyöntien poisto — se myös yhdistää tuplasäännöt, muuntaa värit lyhyempiin muotoihin (#ff0000 muuttuu #f00:ksi tai jopa red:ksi), poistaa redundantit propertyt ja optimoi calc()-lausekkeet. Tyypillisellä tyylitiedostolla voit odottaa 30-50% säästöjä.

css

HTML:lle html-minifier-terser poistaa välilyöntejä tagien välistä, poistaa valinnaiset sulkutagit, minifioi inline CSS:n ja JS:n, poistaa HTML-kommentit ja tiivistää boolean-attribuutit. Se on yllättävän tehokas — olen nähnyt 20-30% pienenemistä HTML-rikkailla sivuilla.

Ja tässä tulee hyvä juttu: jos käytät Angularia, Reactia, Vueta tai käytännössä mitä tahansa modernia kehystä build-vaiheella, tämä tapahtuu jo automaattisesti. Build-työkalusi hoitaa CSS- ja HTML-minifioinnin osana tuotantobuildia. Mutta tieto siitä mitä konepellin alla tapahtuu on aidosti hyödyllistä — varsinkin kun sinun täytyy debugata miksi tuotanto-HTML ei vastaa lähdekoodiasi, tai kun yrität optimoida viimeistä kriittistä renderöintipolkua.

Koodin muotoilu CI/CD-putkissa

Kuule, tässä on se juttu "muotoile tallennettaessa" -ajattelussa — se toimii vain, jos *jokaisella tiimin jäsenellä* on se konfiguroitu oikein. Ja tämä on puraissut minua ennenkin. Tiedät skenaarion: joku liittyy tiimiin, kloonaa repon, alkaa tehdä muutoksia ja lähettää PR:n, jossa on 400 muotoilumuutosta sekoitettuna 5 riviin oikeaa koodia. Tuon PR:n katselmointi on painajainen.

Ratkaisu? Pakota muotoilu CI:ssä. Tee mahdottomaksi mergetä muotoilematonta koodia. Näin se tehdään:

Vaihe 1: Lisää CI-tarkistus. Lisää prettier --check . CI-putkeesi. Se päättyy koodilla 1, jos mikä tahansa tiedosto ei vastaa Prettierin muotoilua. Ei argumentteja, ei väittelyitä — CI on laki.

javascript

Vaihe 2: Lisää pre-commit-hookit. Muotoiluongelmien nappaaminen CI:ssä on hienoa, mutta vielä parempi on napata ne *ennen* kuin koodi on edes commitoitu. Tässä kohtaa Husky ja lint-staged astuvat kuvaan.

javascript
javascript
javascript

lint-stagedin kauneus on siinä, että se ajaa vain oikeasti muuttamillasi tiedostoilla, ei koko koodikannalla. Joten pre-commit-hookki kestää 1-2 sekuntia 30:n sijaan. Kukaan ei poista käytöstä hookkia, joka kestää 1 sekunnin.

Olen nähnyt tiimien siirtyvän "muotoilu on jatkuva PR-kitkan lähde" -tilasta "emme kirjaimellisesti koskaan ajattele muotoilua" -tilaan viikossa tämän pystyttämisen jälkeen. Se on yksi niistä asioista, joissa 15 minuutin asennus maksaa itsensä takaisin tuhansia kertoja.

Todellisen maailman bundlekoko-tapaustutkimukset

Puhutaan oikeista luvuista, koska abstrakti neuvo "pidä bundlet pieninä" ei ole kovin hyödyllistä ilman kontekstia. Olen nähnyt juuri nämä skenaariot toteutuvan tuotantoprojekteissa, ja erot ovat aidosti järkyttäviä.

Tapaus 1: lodash vs lodash-es. Tämä on se klassikko. Jos teet import _ from 'lodash' ja käytät vain _.debounce, tuot koko kirjaston — 71,5 KB minifioituna + gzipattuna. Vaihda import { debounce } from 'lodash-es':ään ja tree shakingin kanssa katsot noin 1,5 KB pelkästään tuolle funktiolle. Se on 98% vähennys. Tarkista itse Bundlephobiasta.

Tapaus 2: moment.js vs dayjs. Moment.js oli päivämääräkäsittelyn kultastandardi vuosia, mutta se painaa 72,1 KB minifioituna + gzipattuna — ja se sisältää kaikki locale-datansa oletuksena. Day.js:llä on lähes identtinen API mutta se painaa 2,9 KB. Se ei ole kirjoitusvirhe. 72 KB vs 3 KB käytännössä samasta toiminnallisuudesta. Moment.js-tiimi itse suosittelee nyt vaihtoehtoja.

Tapaus 3: Ikonikirjastot. Tämä yllättää ihmisiä jatkuvasti. Jos teet import { FaHome } from 'react-icons/fa', kaikki on hyvin — se on tree-shakeable. Mutta jotkut ikonikirjastot eivät ole asetettu tree shakingia varten, ja päädyt tuomaan satoja SVG-ikoneja kun tarvitset vain 5. Olen nähnyt ikoni-importtien lisäävän 200+ KB bundleihin. Varmista aina, että ikonikirjastosi tukee tree shakingia, tai tuo ikonit yksitellen.

Tapaus 4: Päivämäärämuotoilukirjastot. Piti muotoilla yksi päivämäärä tietyssä aikavyöhykkeessä? Näin kerran projektin, joka toi moment-timezone:n (täyden version kaikilla aikavyöhykedatoilla) — se on 97 KB minifioituna + gzipattuna — pelkästään yhden päivämäärän muotoiluun. Natiivi Intl.DateTimeFormat-API hoitaa suurimman osan aikavyöhykemuotoilusta natiivisti ilman bundlekustannusta.

Opetus tässä ei ole "älä koskaan käytä riippuvuuksia" — se olisi typerää. Opetus on: tiedä mitä tuot ja paljonko se maksaa. Aja npx source-map-explorer build/static/js/*.js tai tarkista Bundlephobia ennen kuin lisäät sen kiiltävän uuden kirjaston. Käyttäjäsi hitailla mobiiliyhteyksillä kiittävät sinua.

Kokeile itse

Pitääkö siivota sotkuista koodia nopeasti? Liitä se JavaScript Formatteriin saadaksesi välittömän, luettavan tuloksen. Valmis kutistamaan sen tuotantoa varten? JavaScript Minifier riisuu sen minimiin. Ja jos et ole varma onko koodisi edes validi, aja se ensin JavaScript Validatorin läpi.