This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2023-10-20
Channels
- # announcements (4)
- # beginners (5)
- # biff (1)
- # calva (2)
- # clj-kondo (32)
- # clojure (50)
- # clojure-czech (6)
- # clojure-europe (14)
- # clojure-nl (1)
- # clojure-norway (77)
- # clojure-uk (2)
- # core-logic (1)
- # cursive (6)
- # datahike (1)
- # datomic (15)
- # emacs (2)
- # events (1)
- # honeysql (1)
- # hyperfiddle (45)
- # introduce-yourself (2)
- # lsp (9)
- # other-lisps (10)
- # overtone (2)
- # polylith (5)
- # practicalli (1)
- # ring (2)
- # shadow-cljs (6)
I dag er det min tur til Ä debutere pÄ ny blogg, og det gjÞr jeg med en liten rant om bakoverkompatibilitet, breaking changes og hvordan vi utviklere bruker tiden vÄr: https://parenteser.mattilsynet.io/bakoverkompatibilitet/
Det var deilig lesning for en stakkar som i den siste tiden har sittet og oppgradert tjenester fra .NET 3.1 â .NET 6, vel vitende om at, sĂ„ fort jeg er ferdig med alle tjenestene, kommer det en ny LTS release av .NET, sĂ„ starter reisen pĂ„ nytt. Recurrere ad infinitum.
I mitt stille sinn tenker jeg at bakoverkompatibilitet ala Rich er lettere Ă„ gjennomfĂžre i Clojure enn i feks ts/js.
Men det handler kanskje mer om idiomer enn sprÄk. Hvis man kun trenger noen keys, bruk kun noen keys. Ikke krasj nÄr det kommer nye.
Men da mÄ man ogsÄ vite nÄr man bÞr bruke select-keys
.
Halve teamet sitter og oppgraderer rammeverk og andre deps kontinuerlig mellom andre oppgaver. Det tar mye tid. Det er en stor ulempe med Ă„ ha ~400 mikrotjenester.
@U3X7174KS Jeg tror det er veldig gjennomfÞrbart i mange flere miljÞer. Men klart, et sterkere typesystem gjÞr dette vanskeligere Ä fÄ til - men ogsÄ noe lettere Ä hÄndtere, ettersom kompilatoren kan guide deg gjennom type-endringer. Men det er fortsatt mulig Ä legge til ting, ikke ta bort ting, osv. LÞsningsforslagene i blogginnlegget fungerer i alle sprÄk.
Hvis man bruker "destructure everything or crash" fÄr man vel problemer en ny attributt legges til. Men da er kanskje svaret ditt. OK, da er Ä legge til en ting en breaking change. Da beholder du det gamle API-et, markerer det som deprekert, skriver om docs, og lager et nytt API ved siden. :thinking_face: Det skjer litt rubber-ducking her. Enig!
Jeg mener ikke at Ă„ legge til ting er en breaking change @U3X7174KS
Fun fact: Erlang/OTP (og Elixir) miljÞet har et slagord "Let It Crash!" (som ofte er misforstÄtt).
@U07FCNURX jeg argumenterer ikke for at "destructure everything or crash" er lurt - men det finnes syntax for det i enkelte sprÄk. Og den brukes. Feks Haskell: http://learnyouahaskell.com/making-our-own-types-and-typeclasses Surface-funksjonen vil krasje hvis det legges til et ekstra felt pÄ Circle.
Det mĂ„ kanskje til for at kompilatoren skal kunne levere pĂ„ garantiene den har gitt? đ€· Men i sĂ„ fall ville jeg si at man mĂ„ i stĂžrre grad shippe nye funksjoner og typer for Ă„ innfri pĂ„ idealet mitt.
Deprekering ..MĂ„tte lese to ganger for Ă„ skjĂžnne hva dette ordet var, men det er kanskje allerede endel brukt? đ âdeprecateâ - to pray away https://www.etymonline.com/search?q=deprecate âBe bortâ, âĂžnske vekkâ? - Hadde vĂŠrt gĂžy med et norsk uttrykk som fanget at vi skal ikke fjerne dette, men vi skulle Ăžnske vi kunne fjerne det.
Jeg liker egentlig pensjonert ganske godt. Det beskriver ikke bare at funksjonen ikke er i arbeid, men ogsÄ at den har vÊrt nyttig en gang i tiden og at det sannsynligvis finnes en arvtager.
Litt relatert til den utmerkede posten over. En diskusjon vi har gĂ„ende i Ardoq er âsuperfluous values in api requests and what to do with themâ. SĂ„ anta et api-endepunkt som fĂžlger:
POST /api/foo {:baz 1}
AltsÄ et endepunkt /api/foo
som tar en body som mÄ ha :baz
i seg.
Hva gjĂžr vi hvis brukeren sender (kaller, poster?) fĂžlgende:
POST /api/foo {:bar 1, :baz 2}
Stripper vi vekk :bar
og godtar requesten og returnerer 201, eller skriker vi âNEI NEI NEI du sendte :bar
som er feilâ og returnerer 400?Det eneste riktige svaret er: "Nothing" (Jeg svarte pĂ„ en kortere melding fĂžr @slipset sleipt redigerte det om til et mer nyansert ett đ
)
Argumentet for 400 er kanskje mer hÄndholding / "la oss behandle API-et som en typesjekk"? Eller?
det er jo kjekt Ă„ fĂ„ en feilmelding om man sender inn âbasâ fordi man skrev âbazâ feil forsĂ„vidt
@slipset jeg liker argumentet for "The web should evolve in a versionless manner" som presentert her: https://www.oilshell.org/blog/2022/03/backlog-arch.html#the-web-evolved-in-a-versionless-manner
Kanskje med unntak av hvis dere har Ă©n backend og Ă©n frontend i ett repo og shipper oppgraderinger av frontend+backend samtidig
@slipset Jeg trodde fĂžrst du snakket om at deres API-er sendte ut overflĂždige verdier đ Men det tilfellet du beskriver der er litt interessant. Jeg vil ogsĂ„ helle mot Ă„ si 201, takk skal du ha. En mellomvei er Ă„ godta endringen, men returnere en payload som sier noe om jobben som ble gjort. Ja, den var vellykket, men du sendte inn noen verdier vi ikke bryr oss om.
Og som @augustl er inne pĂ„ - dersom requesten faktisk er feil kan man jo virkelig fingerspitzgefĂŒhle lĂžsningen Ă„ si "dette gĂ„r ikke, men om du renamer den bar-en din til baz sĂ„ blir det fest"
Jeg har likt tanken pÄ Ä returnere informasjon om hva som ble rettet pÄ, men ikke funnet mediet. Men, en ekstra header pÄ responsen kan bÊre en mulighet.
men hvis returverdien er et mapâŠ. đ (Apropos: i de siste Ă„rene har jeg begynt Ă„ pakke nesten alle returverdier fra API-er inn i maps, nettopp sĂ„ jeg kan legge til flere ting senere uten Ă„ brekke callers)
De er jo det, men:
POST /api/user
returnerer idag et map som representerer en user
. Det synes uelegant Ă„ legge til nye attributter som ikke hĂžrer til user
i det mapâet. Vi har litt det samme problemet med
GET /api/user
som returnerer alle users. Det var sikkert fint den gangen man hadde tre av dem, men det hadde vÊrt fint Ä gjort det med paginering nÄr man fÄr fler. Det er vanskelig Ä bolte pÄ det i ettertid.
enig - captain hindsight sier at man kunne returnert {user: {...}}
, fÞles ofte litt overkill i nÄtiden, men har aldri angret pÄ det i ettertiden
Jeg vet ikke hvor mange ganger jeg har returnert en liste fra et API for litt senere Ä angre bittert pÄ akkurat det
Mulig det hÞrer til Ä i samme kategori at man alltid bÞr ha en "envelope" rundt maps ogsÄ, som @augustl foreslÄr
Jeg syns man skal tenke seg godt om fĂžr man gjĂžr accretion om til en breaking change. Da har man fint lite verktĂžy igjen i skuffen sin.
Jeg liker Rich Hickey sin definisjon av bakoverkompatibilitet (SJOKK!): du kan gi mer og kreve mindre. Med den rettesnoren er overflĂždige felter i en POST helt klart innenfor.
Si at du har et endepunkt som tar noe som tilfredsstiller denne specâen:
(s/def ::data (s/keys :req-un [::foo] :opt-un [::bar]))
Hvis jeg nÄ fatfingrer og sender en body som:
{:foo 1 :bae 2}
Hvor jeg egentlig ville sende :bar
sÄ ville en strategi der vi bare luker ut attributter vi ikke vil ha skjule en feil for meg.Kanskje en mulighet kunne vÊre Ä ha et strengt validerings-endepunkt? AltsÄ en opt-in streng sjekk. Vet ikke om det har noe for seg egentlig.
har Rich Hickey egentlig noen gang sagt noe jeg er uenig med ham i :thinking_face: Nesten et rĂždt flagg, mĂ„ jo vĂŠre noe đ
Rich velger jo ofte Ă„ uttale seg smalt om spesifikke problemer. Den typen meninger er lettere Ă„ treffe pĂ„ enn hvis du uttaler deg veldig bredt, eller prĂžver Ă„ lĂžse alle verdens problemer med Ă©n ny dings. Skulle nesten tro han tenke bakoverkompatibilitet i meninger og pĂ„stander! đ
har litt forksjellig magefÞlelse pÄ query params og body-data og, av en eller annen grunn. Ville fÄtt sjokk om jeg ikke kunne sende med ekstra query params, men kunne levd med Ä fÄ feil om jeg sendte inn ukjente felt i JSON
Det finnes en lov for dette! Postelâs law: «be conservative in what you send, be liberal in what you accept» (https://en.wikipedia.org/wiki/Robustness_principle). Jeg er definitivt pĂ„ team 201 i denne saken. En select-keys
pÄ dataene du vil akseptere og ferdig med det.
Ville fĂ„tt sjokk om jeg ikke kunne sende med ekstra query params, men kunne levd med Ă„ fĂ„ feil om jeg sendte inn ukjente felt i JSONKanskje bare det er hva man er vant til at skjer? Query params jobber man fort med som et map (i alle fall i PHP). mens body er det vanlig Ă„ parse inn til en type, og noen âparse json til typeâ-funksjoner krasjer hvis man har med felter som ikke er definert i typene?
Jeg har lest meg opp pÄ- og lekt litt med Compojure i dag. Dette synes jeg var ganske nyttig!
Fra server
namespace:
(c/defroutes app
(c/context "/htmx-demo" []
(c/context "/button" []
(c/GET "/" [] (handlers/htmx-demo-button))
(c/POST "/clicked" [] (handlers/htmx-demo-button-clicked))))
(r/not-found (handlers/not-found)))
Fra content
namespace:
(def htmx-demo-button
[:section.section
[:div.container
[:h1.title "HTMX Demo"]
[:button.button {:hx-post "/htmx-demo/button/clicked"
:hx-swap "outerHTML"}
"Click Me!"]]])
Fra handlers
namespace:
(defn htmx-demo-button []
{:status 200
:headers http-headers-default
:body (build-page content/htmx-demo-button)})
(defn htmx-demo-button-clicked []
{:status 200
:headers http-headers-default
:body [:p "Thank You!"]})
PÄ den mÄten kan man bygge opp en mer "logisk" og selvdokumenterende hierarki av routes.
Selvsagt overkill for min lille app enn sÄ lenge, men det var gÞy Ä lÊre om det.Ulempen med den konteksten er at nÄ finnes ikke strengen "/html-demo/button/clicked" noe sted i rutene din lengre
Ikke et kjempeproblem i den stÞrrelsesordenen der, men gi det litt tid, sÄ har det vokst og deler av rutene er flytta ut i egne filer osv. Da kan det bli litt krÞkkete.
Noen av dem finnes i content
namespace:
(def htmx-demo-button
[:section.section
[:div.container
[:h1.title "HTMX Demo"]
[:button.button {:hx-post "/htmx-demo/button/clicked"
:hx-swap "outerHTML"}
"Click Me!"]]])
Men da som en hÄndskrevet streng til hx-post
i HTML-en.Og det er litt irriterende Ä mÄtte se pÄ routeren og tenke ut strukturen for Ä skrive inn den strengen i hx-post
.
Jeg tenker at dersom det kun er for Ä klippe opp URL-en er det ikke verdt det, men dersom du vil bruke konteksten til Ä ha autentisering pÄ et sett med ruter eller lignende kan det vÊre verdt det. Men dette er litt det jeg snakket om da jeg nevnte at jeg syns routing-biblioteker har det med Ä bli for clever. Det er veldig lett Ä legge litt for mye funksjonalitet inn der som kanskje hÞrer bedre hjemme andre steder.
Interessant trade-off egentlig: mer logisk strukturerte/penere paths vs. enklere Ă„ lese/jobbe med koden.
Jeg var veldig glad i sÄnt fÞr, men etter Ä ha slitt med Ä finne igjen ting i sÄnn kode litt for mange ganger har jeg lÊrt meg Ä sette mer og mer pris pÄ Ä optimalisere for at koden skal leses nÄr jeg har glemt alle de gode idéene jeg hadde da jeg skrev den.