Registroj de la antaŭfina programisto Habr: refactoring kaj reflektado

Registroj de la antaŭfina programisto Habr: refactoring kaj reflektado

Mi ĉiam interesiĝis pri kiel Habr estas strukturita de interne, kiel la laborfluo estas strukturita, kiel komunikadoj estas strukturitaj, kiaj normoj estas uzataj kaj kiel la kodo estas skribita ĉi tie ĝenerale. Feliĉe, mi ricevis ĉi tiun ŝancon, ĉar mi ĵus fariĝis parto de la habra teamo. Uzante la ekzemplon de malgranda refaktorado de la poŝtelefona versio, mi provos respondi la demandon: kiel estas labori ĉe la unua linio ĉi tie? En la programo: Nodo, Vue, Vuex kaj SSR kun saŭco el notoj pri persona sperto en Habr.

La unua afero, kiun vi devas scii pri la evolua teamo, estas, ke ni estas malgrandaj. Malmulto estas tri frontoj, du malantaŭoj kaj la teknika antaŭeco de ĉiuj Habr - Baxley. Ekzistas, kompreneble, ankaŭ testisto, dezajnisto, tri Vadim, mirakla balailo, merkatisto kaj aliaj Bumburum'oj. Sed estas nur ses rektaj kontribuantoj al la retejoj de Habr. Ĉi tio estas sufiĉe malofta - projekto kun multmilion-dolara publiko, kiu ekstere aspektas kiel giganta entrepreno, fakte pli similas al komforta starto kun la plej plata organiza strukturo.

Kiel multaj aliaj IT-kompanioj, Habr konfesas Agilajn ideojn, CI-praktikojn, kaj jen ĉio. Sed laŭ mi, Habr kiel produkto evoluas en ondoj prefere ol kontinue. Do, dum pluraj sinsekvaj spurtoj, ni diligente kodas ion, desegnas kaj restrukturas, rompas ion kaj riparas ĝin, solvas biletojn kaj kreas novajn, paŝas sur rastilon kaj pafas nin en la piedojn por finfine liberigi la funkcion en la estonteco. Kaj tiam venas certa paŭzo, periodo de renovigo, tempo por fari tion, kio estas en la "grava-ne urĝa" kvadranto.

Ĝuste ĉi tiu "ekstersezona" spurto estos diskutita sube. Ĉi-foje ĝi inkludis refaktorigon de la movebla versio de Habr. Ĝenerale, la kompanio havas grandajn esperojn pri ĝi, kaj estonte ĝi devus anstataŭigi la tutan zoon de la enkarniĝoj de Habr kaj fariĝi universala transplatforma solvo. Iam estos adapta aranĝo, PWA, eksterreta reĝimo, uzanta personigo kaj multaj aliaj interesaj aferoj.

Ni starigu la taskon

Iam, ĉe ordinara stand-up, unu el la fronto parolis pri problemoj en la arkitekturo de la komentoj komponanto de la movebla versio. Kun ĉi tiu sugesto, ni organizis mikro-renkontiĝon en la formato de grupa psikoterapio. Ĉiuj laŭvice diris kie doloras, ili registris ĉion surpapere, simpatiis, komprenis, krom ke neniu aplaŭdis. La rezulto estis listo de 20 problemoj, kiuj klarigis, ke poŝtelefono Habr ankoraŭ havis longan kaj dornan vojon al sukceso.

Mi ĉefe zorgis pri rimeda efikeco kaj tio, kion oni nomas glata interfaco. Ĉiutage, sur la vojo "hejm-laboro-hejmo", mi vidis mian malnovan telefonon senespere provi montri 20 titolojn en la feed. Ĝi aspektis kiel ĉi tio:

Registroj de la antaŭfina programisto Habr: refactoring kaj reflektadoPoŝtelefona Habr-interfaco antaŭ refactoring

Kio okazas ĉi tie? Resume, la servilo servis la HTML-paĝon al ĉiuj same, sendepende de ĉu la uzanto estis ensalutinta aŭ ne. Tiam la kliento JS estas ŝarĝita kaj denove petas la necesajn datumojn, sed ĉi-foje ĝustigitaj por rajtigo. Tio estas, ni efektive faris la saman laboron dufoje. La interfaco flagris, kaj la uzanto elŝutis bone cent kromajn kilobajtojn. En la detaloj, ĉio aspektis eĉ pli timiga.

Registroj de la antaŭfina programisto Habr: refactoring kaj reflektadoMalnova SSR-CSR-skemo. Rajtigo estas ebla nur en stadioj C3 kaj C4, kiam Node JS ne estas okupata generante HTML kaj povas prokurigi petojn al la API.

Nia tiama arkitekturo estis tre precize priskribita de unu el la uzantoj de Habr:

La movebla versio estas aĉa. Mi diras ĝin tia, kia ĝi estas. Terura kombinaĵo de SSR kune kun CSR.

Ni estis devigitaj konfesi tion, kiom ajn malĝoja ĝi estis.

Mi taksis la eblojn, kreis bileton en Jira kun priskribo je la nivelo de "estas malbona nun, faru ĝin en ordo" kaj malkomponis la taskon laŭ larĝe:

  • reuzi datumojn,
  • minimumigi la nombron da redesegnaĵoj,
  • forigi duplikatajn petojn,
  • fari la ŝarĝan procezon pli evidenta.

Ni reuzu la datumojn

En teorio, servilflanka bildigo estas dizajnita por solvi du problemojn: ne suferi pro serĉmotorlimigoj en terminoj de SPA-indeksado kaj plibonigi metrikojn FMP (neeviteble plimalboniĝas TTI). En la klasika scenaro tio finfine formulite ĉe Airbnb en 2013 jaro (reen sur Backbone.js), SSR estas la sama izomorfa JS-aplikaĵo funkcianta en la Node-medio. La servilo simple resendas la generitan aranĝon kiel respondon al la peto. Tiam rehidratiĝo okazas ĉe la klienta flanko, kaj tiam ĉio funkcias sen paĝaj reŝargiĝoj. Por Habr, kiel por multaj aliaj rimedoj kun tekstenhavo, servila bildigo estas kritika elemento por konstrui amikecajn rilatojn kun serĉiloj.

Malgraŭ tio, ke pli ol ses jaroj pasis de kiam la teknologio aperis, kaj dum ĉi tiu tempo multe da akvo pasis sub la ponto en la front-end-mondo, por multaj programistoj ĉi tiu ideo ankoraŭ estas kovrita en sekreto. Ni ne flankenmetis kaj lanĉis Vue-aplikaĵon kun SSR-subteno al produktado, mankis unu malgranda detalo: ni ne transdonis la komencan staton al la kliento.

Kial? Ne ekzistas preciza respondo al ĉi tiu demando. Aŭ ili ne volis pliigi la grandecon de la respondo de la servilo, aŭ pro amaso da aliaj arkitekturaj problemoj, aŭ ĝi simple ne ekis. Unu maniero aŭ alia, forĵeti la ŝtaton kaj reuzi ĉion, kion faris la servilo, ŝajnas sufiĉe taŭga kaj utila. La tasko estas fakte bagatela. stato estas simple injektita en la ekzekutkuntekston, kaj Vue aŭtomate aldonas ĝin al la generita aranĝo kiel tutmonda variablo: window.__INITIAL_STATE__.

Unu el la problemoj kiuj aperis estas la malkapablo konverti ciklajn strukturojn en JSON (cirkla referenco); estis solvita simple anstataŭigante tiajn strukturojn kun iliaj plataj ekvivalentoj.

Krome, kiam vi traktas UGC-enhavon, vi devas memori, ke la datumoj devas esti konvertitaj al HTML-unuoj por ne rompi HTML-on. Por ĉi tiuj celoj ni uzas he.

Minimumigante redesegnaĵojn

Kiel vi povas vidi de la supra diagramo, en nia kazo, unu Node JS-instanco plenumas du funkciojn: SSR kaj "proxy" en la API, kie okazas uzantrajtigo. Ĉi tiu cirkonstanco malebligas rajtigi dum la JS-kodo funkcias sur la servilo, ĉar la nodo estas unufadena, kaj la SSR-funkcio estas sinkrona. Tio estas, la servilo simple ne povas sendi petojn al si mem dum la vokstako estas okupata de io. Evidentiĝis, ke ni ĝisdatigis la staton, sed la interfaco ne ĉesis svingiĝi, ĉar la datumoj pri la kliento devis esti ĝisdatigitaj konsiderante la uzantan sesion. Necesis instrui nian aplikaĵon meti la ĝustajn datumojn en la komencan staton, konsiderante la ensaluton de la uzanto.

Ekzistis nur du solvoj al la problemo:

  • ligi rajtigajn datumojn al trans-servilaj petoj;
  • dividu Node JS-tavolojn en du apartajn okazojn.

La unua solvo postulis la uzon de tutmondaj variabloj sur la servilo, kaj la dua plilongigis la tempokadron por plenumi la taskon almenaŭ unu monaton.

Kiel fari elekton? Habr ofte moviĝas laŭ la vojo de malplej rezisto. Neformale, ekzistas ĝenerala deziro redukti la ciklon de ideo al prototipo al minimumo. La modelo de sinteno al la produkto estas iom rememoriga pri la postulatoj de booking.com, kun la nura diferenco estas, ke Habr multe pli serioze prenas uzantajn komentojn kaj konfidas tiajn decidojn al vi kiel programisto.

Sekvante ĉi tiun logikon kaj mian propran deziron rapide solvi la problemon, mi elektis tutmondajn variablojn. Kaj, kiel ofte okazas, vi devas pagi por ili frue aŭ malfrue. Ni preskaŭ tuj pagis: ni laboris semajnfine, klarigis la sekvojn, skribis postmortem kaj komencis dividi la servilon en du partojn. La eraro estis tre stulta, kaj la cimo implikanta ĝin ne estis facile reproduktebla. Kaj jes, estas domaĝe por ĉi tio, sed iel aŭ alie, stumblante kaj ĝemante, mia PoC kun tutmondaj variabloj tamen eniris en produktadon kaj funkcias sufiĉe sukcese atendante la movon al nova "du-noda" arkitekturo. Ĉi tio estis grava paŝo, ĉar formale la celo estis atingita - SSR lernis liveri tute uzeblan paĝon, kaj la UI fariĝis multe pli trankvila.

Registroj de la antaŭfina programisto Habr: refactoring kaj reflektadoMobile Habr-interfaco post la unua etapo de refactoring

Finfine, la arkitekturo SSR-CSR de la movebla versio kondukas al la sekva bildo:

Registroj de la antaŭfina programisto Habr: refactoring kaj reflektado"Du-noda" SSR-CSR-cirkvito. Node JS API ĉiam estas preta por nesinkrona I/O kaj ne estas blokita de la SSR-funkcio, ĉar ĉi-lasta situas en aparta kazo. Demandĉeno #3 ne estas bezonata.

Forigi duplikatajn petojn

Post kiam la manipuladoj estis faritaj, la komenca bildigo de la paĝo ne plu provokis epilepsion. Sed la plua uzo de Habr en SPA-reĝimo daŭre kaŭzis konfuzon.

Ĉar la bazo de uzantfluo estas transiroj de la formo listo de artikoloj → artikolo → komentoj kaj male, estis grave optimumigi la resursan konsumon de ĉi tiu ĉeno unue.

Registroj de la antaŭfina programisto Habr: refactoring kaj reflektadoReveno al la afiŝo-fluo ekigas novan datumpeton

Ne necesis fosi profunde. En la ekrankando supre vi povas vidi, ke la aplikaĵo re-petas la liston de artikoloj kiam svingas reen, kaj dum la peto ni ne vidas la artikolojn, kio signifas, ke la antaŭaj datumoj malaperas ie. Ŝajnas, ke la artikola listo uzas lokan ŝtaton kaj perdas ĝin por detrui. Fakte, la aplikaĵo uzis tutmondan staton, sed la arkitekturo Vuex estis konstruita fronte: moduloj estas ligitaj al paĝoj, kiuj siavice estas ligitaj al itineroj. Krome, ĉiuj moduloj estas "unufojaj" - ĉiu posta vizito al la paĝo reverkis la tutan modulon:

ArticlesList: [
  { Article1 },
  ...
],
PageArticle: { ArticleFull1 },

Entute ni havis modulon Listo de Artikoloj, kiu enhavas objektojn de tipo artikolo kaj modulo PaĝoArtikolo, kiu estis plilongigita versio de la objekto artikolo, ia Artikolo Plena. Ĝenerale, ĉi tiu efektivigo ne portas en si ion teruran - ĝi estas tre simpla, oni eĉ povus diri naiva, sed ege komprenebla. Se vi restarigas la modulon ĉiufoje kiam vi ŝanĝas la itineron, tiam vi eĉ povas vivi kun ĝi. Tamen, la transiro inter artikolo-fluoj, ekzemple /feed → /all, estas garantiita forĵeti ĉion rilate al via persona nutrado, ĉar ni havas nur unu Listo de Artikoloj, en kiun vi devas meti novajn datumojn. Ĉi tio denove kondukas nin al duobligo de petoj.

Kolektinte ĉion, kion mi sukcesis elfosi pri la temo, mi formulis novan ŝtatan strukturon kaj prezentis ĝin al miaj kolegoj. La diskutoj estis longaj, sed finfine la favoraj argumentoj superpezis la dubojn, kaj mi komencis efektivigon.

La logiko de decido estas plej bone malkaŝita en du etapoj. Unue ni provas malkunligi la Vuex-modulon de la paĝoj kaj ligi rekte al la itineroj. Jes, estos iom pli da datumoj en la vendejo, getters fariĝos iom pli kompleksaj, sed ni ne ŝargos artikolojn dufoje. Por la movebla versio, ĉi tio eble estas la plej forta argumento. Ĝi aspektos kiel ĉi tio:

ArticlesList: {
  ROUTE_FEED: [ 
    { Article1 },
    ...
  ],
  ROUTE_ALL: [ 
    { Article2 },
    ...
  ],
}

Sed kio se la listoj de artikoloj povas interkovri inter pluraj vojoj kaj kio se ni volas reuzi la objektodatenojn artikolo por redoni la postpaĝon, igante ĝin Artikolo Plena? En ĉi tiu kazo, estus pli logike uzi tian strukturon:

ArticlesIds: {
  ROUTE_FEED: [ '1', ... ],
  ROUTE_ALL: [ '1', '2', ... ],
},
ArticlesList: {
  '1': { Article1 }, 
  '2': { Article2 },
  ...
}

Listo de Artikoloj jen nur speco de deponejo de artikoloj. Ĉiuj artikoloj kiuj estis elŝutitaj dum la uzantsesio. Ni traktas ilin kun la plej granda zorgo, ĉar tio estas trafiko, kiu eble estis elŝutita ie en la metroo inter stacioj, kaj ni certe ne volas kaŭzi ĉi tiun doloron denove al la uzanto devigante lin ŝargi datumojn, kiujn li jam elŝutis. . Objekto ArtikolojIds estas simple aro de identigiloj (kiel "ligiloj") al objektoj artikolo. Ĉi tiu strukturo ebligas al vi eviti duobligi datumojn komunajn al itineroj kaj reuzi la objekton artikolo dum afiŝpaĝo kunfandante plilongigitajn datumojn en ĝin.

La eligo de la listo de artikoloj ankaŭ fariĝis pli travidebla: la iteratorkomponento ripetas tra la tabelo kun artikolidentigiloj kaj desegnas la artikolteaserkomponenton, pasante la ID kiel apogilon, kaj la infankomponento, siavice, prenas la necesajn datumojn de Listo de Artikoloj. Kiam vi iras al la publikigpaĝo, ni prenas la ekzistantan daton de Listo de Artikoloj, ni petas ricevi la mankantajn datumojn kaj simple aldoni ĝin al la ekzistanta objekto.

Kial ĉi tiu aliro estas pli bona? Kiel mi skribis supre, ĉi tiu aliro estas pli zorgema rilate al elŝutitaj datumoj kaj permesas vin reuzi ĝin. Sed krom tio, ĝi malfermas la vojon al iuj novaj eblecoj, kiuj perfekte persvadas en tia arkitekturo. Ekzemple, balotado kaj ŝarĝo de artikoloj en la feed kiel ili aperas. Ni povas simple meti la lastajn afiŝojn en "stokadon" Listo de Artikoloj, konservu apartan liston de novaj identigiloj enen ArtikolojIds kaj sciigu la uzanton pri tio. Kiam ni alklakas la butonon "Montri novajn publikaĵojn", ni simple enmetos novajn identigilojn en la komencon de la tabelo de la nuna listo de artikoloj kaj ĉio funkcios preskaŭ magie.

Farante elŝutadon pli agrabla

La glaciaĵo sur la refaktora kuko estas la koncepto de skeletoj, kiu faras la procezon de ŝarĝo de enhavo sur malrapida interreto iomete malpli frustra. Ne estis diskutoj pri ĉi tiu afero; la vojo de ideo al prototipo daŭris laŭlitere du horojn. La dezajno praktike desegnis sin, kaj ni instruis niajn komponantojn fari simplajn, apenaŭ flagrantajn div-blokojn atendante datumojn. Subjektive, ĉi tiu aliro al ŝarĝo efektive reduktas la kvanton de stresaj hormonoj en la korpo de la uzanto. La skeleto aspektas jene:

Registroj de la antaŭfina programisto Habr: refactoring kaj reflektado
Habraloading

Pripensante

Mi laboras en Habr de ses monatoj kaj miaj amikoj ankoraŭ demandas: nu, kiel vi ŝatas tie? Bone, komforta - jes. Sed estas io, kiu distingas ĉi tiun laboron de aliaj. Mi laboris en teamoj kiuj estis tute indiferentaj pri sia produkto, ne sciis aŭ komprenis kiuj estas iliaj uzantoj. Sed ĉi tie ĉio estas malsama. Ĉi tie vi sentas vin respondeca pri tio, kion vi faras. En la procezo de disvolviĝo de funkcio, vi parte fariĝas ĝia posedanto, partoprenas en ĉiuj produktaj kunvenoj rilataj al via funkcio, faras sugestojn kaj mem decidas. Fari produkton, kiun vi uzas ĉiutage, mem estas tre bonega, kaj skribi kodon por homoj, kiuj verŝajne komprenas ĝin pli bone ol vi, estas nur nekredebla sento (sen sarkasmo).

Post la liberigo de ĉiuj ĉi tiuj ŝanĝoj, ni ricevis pozitivajn reagojn, kaj ĝi estis tre, tre bela. Ĝi estas inspira. Dankon! Skribu pli.

Mi memorigu vin, ke post tutmondaj variabloj, ni decidis ŝanĝi la arkitekturon kaj asigni la prokuran tavolon en apartan okazon. La "du-noda" arkitekturo jam atingis liberigon en formo de publika beta-testado. Nun ĉiu povas ŝanĝi al ĝi kaj helpi nin plibonigi poŝtelefonon Habr. Tio estas ĉio por hodiaŭ. Mi volonte respondos ĉiujn viajn demandojn en la komentoj.

fonto: www.habr.com

Aldoni komenton