[{"data":1,"prerenderedAt":588},["ShallowReactive",2],{"docs-nav:fr":3,"docs:\u002Ffr\u002Fdocs\u002Fschema\u002Fevolution":189},[4],{"title":5,"path":6,"stem":7,"children":8,"page":52},"Fr","\u002Ffr","fr",[9],{"title":10,"path":11,"stem":12,"children":13},"Qu'est-ce que Fougere","\u002Ffr\u002Fdocs","fr\u002Fdocs\u002Findex",[14,15,19,23,27,31,53,78,123,148],{"title":10,"path":11,"stem":12},{"title":16,"path":17,"stem":18},"Démarrer","\u002Ffr\u002Fdocs\u002Fgetting-started","fr\u002Fdocs\u002F01.getting-started",{"title":20,"path":21,"stem":22},"Une app que vous avez déjà","\u002Ffr\u002Fdocs\u002Fexisting-app","fr\u002Fdocs\u002F02.existing-app",{"title":24,"path":25,"stem":26},"Apportez votre schéma","\u002Ffr\u002Fdocs\u002Fbring-your-schema","fr\u002Fdocs\u002F03.bring-your-schema",{"title":28,"path":29,"stem":30},"La CLI","\u002Ffr\u002Fdocs\u002Fcli","fr\u002Fdocs\u002F04.cli",{"title":32,"path":33,"stem":34,"children":35,"page":52},"Concepts","\u002Ffr\u002Fdocs\u002Fconcepts","fr\u002Fdocs\u002F2.concepts",[36,40,44,48],{"title":37,"path":38,"stem":39},"Philosophie","\u002Ffr\u002Fdocs\u002Fconcepts\u002Fphilosophy","fr\u002Fdocs\u002F2.concepts\u002F1.philosophy",{"title":41,"path":42,"stem":43},"La Frond","\u002Ffr\u002Fdocs\u002Fconcepts\u002Ffrond","fr\u002Fdocs\u002F2.concepts\u002F2.frond",{"title":45,"path":46,"stem":47},"Le chemin le plus court","\u002Ffr\u002Fdocs\u002Fconcepts\u002Fshortest-path","fr\u002Fdocs\u002F2.concepts\u002F3.shortest-path",{"title":49,"path":50,"stem":51},"Le socle","\u002Ffr\u002Fdocs\u002Fconcepts\u002Fthe-base","fr\u002Fdocs\u002F2.concepts\u002F4.the-base",false,{"title":54,"path":55,"stem":56,"children":57,"page":52},"Schéma","\u002Ffr\u002Fdocs\u002Fschema","fr\u002Fdocs\u002F3.schema",[58,62,66,70,74],{"title":59,"path":60,"stem":61},"Entités","\u002Ffr\u002Fdocs\u002Fschema\u002Fentities","fr\u002Fdocs\u002F3.schema\u002F1.entities",{"title":63,"path":64,"stem":65},"Vues","\u002Ffr\u002Fdocs\u002Fschema\u002Fviews","fr\u002Fdocs\u002F3.schema\u002F2.views",{"title":67,"path":68,"stem":69},"La carte d'identité","\u002Ffr\u002Fdocs\u002Fschema\u002Fcard","fr\u002Fdocs\u002F3.schema\u002F3.card",{"title":71,"path":72,"stem":73},"Standard Schema","\u002Ffr\u002Fdocs\u002Fschema\u002Fstandard-schema","fr\u002Fdocs\u002F3.schema\u002F4.standard-schema",{"title":75,"path":76,"stem":77},"Comment un schéma bouge","\u002Ffr\u002Fdocs\u002Fschema\u002Fevolution","fr\u002Fdocs\u002F3.schema\u002F5.evolution",{"title":79,"path":80,"stem":81,"children":82,"page":52},"Métier","\u002Ffr\u002Fdocs\u002Fbusiness","fr\u002Fdocs\u002F4.business",[83,87,91,95,99,103,107,111,115,119],{"title":84,"path":85,"stem":86},"Handlers","\u002Ffr\u002Fdocs\u002Fbusiness\u002Fhandlers","fr\u002Fdocs\u002F4.business\u002F1.handlers",{"title":88,"path":89,"stem":90},"Les ports","\u002Ffr\u002Fdocs\u002Fbusiness\u002Fports","fr\u002Fdocs\u002F4.business\u002F10.ports",{"title":92,"path":93,"stem":94},"Presenters","\u002Ffr\u002Fdocs\u002Fbusiness\u002Fpresenters","fr\u002Fdocs\u002F4.business\u002F2.presenters",{"title":96,"path":97,"stem":98},"Collectors","\u002Ffr\u002Fdocs\u002Fbusiness\u002Fcollectors","fr\u002Fdocs\u002F4.business\u002F3.collectors",{"title":100,"path":101,"stem":102},"Erreurs","\u002Ffr\u002Fdocs\u002Fbusiness\u002Ferrors","fr\u002Fdocs\u002F4.business\u002F4.errors",{"title":104,"path":105,"stem":106},"Seeds","\u002Ffr\u002Fdocs\u002Fbusiness\u002Fseeds","fr\u002Fdocs\u002F4.business\u002F5.seeds",{"title":108,"path":109,"stem":110},"Les faits","\u002Ffr\u002Fdocs\u002Fbusiness\u002Ffacts","fr\u002Fdocs\u002F4.business\u002F6.facts",{"title":112,"path":113,"stem":114},"Le port Storage","\u002Ffr\u002Fdocs\u002Fbusiness\u002Fstorage","fr\u002Fdocs\u002F4.business\u002F7.storage",{"title":116,"path":117,"stem":118},"Des écritures qui tiennent ou tombent ensemble","\u002Ffr\u002Fdocs\u002Fbusiness\u002Ftogether","fr\u002Fdocs\u002F4.business\u002F8.together",{"title":120,"path":121,"stem":122},"Les repositories","\u002Ffr\u002Fdocs\u002Fbusiness\u002Frepositories","fr\u002Fdocs\u002F4.business\u002F9.repositories",{"title":124,"path":125,"stem":126,"children":127,"page":52},"Client","\u002Ffr\u002Fdocs\u002Fclient","fr\u002Fdocs\u002F5.client",[128,132,136,140,144],{"title":129,"path":130,"stem":131},"Queries & commands","\u002Ffr\u002Fdocs\u002Fclient\u002Fqueries-commands","fr\u002Fdocs\u002F5.client\u002F1.queries-commands",{"title":133,"path":134,"stem":135},"Formulaires","\u002Ffr\u002Fdocs\u002Fclient\u002Fforms","fr\u002Fdocs\u002F5.client\u002F2.forms",{"title":137,"path":138,"stem":139},"Session","\u002Ffr\u002Fdocs\u002Fclient\u002Fsession","fr\u002Fdocs\u002F5.client\u002F3.session",{"title":141,"path":142,"stem":143},"invoke","\u002Ffr\u002Fdocs\u002Fclient\u002Finvoke","fr\u002Fdocs\u002F5.client\u002F4.invoke",{"title":145,"path":146,"stem":147},"Back-office","\u002Ffr\u002Fdocs\u002Fclient\u002Fadmin","fr\u002Fdocs\u002F5.client\u002F5.admin",{"title":149,"path":150,"stem":151,"children":152,"page":52},"Infrastructure","\u002Ffr\u002Fdocs\u002Finfra","fr\u002Fdocs\u002F6.infra",[153,157,161,165,169,173,177,181,185],{"title":154,"path":155,"stem":156},"Le gradient","\u002Ffr\u002Fdocs\u002Finfra\u002Fgradient","fr\u002Fdocs\u002F6.infra\u002F1.gradient",{"title":158,"path":159,"stem":160},"Surfaces — REST & GraphQL","\u002Ffr\u002Fdocs\u002Finfra\u002Fsurfaces","fr\u002Fdocs\u002F6.infra\u002F2.surfaces",{"title":162,"path":163,"stem":164},"Déploiement","\u002Ffr\u002Fdocs\u002Finfra\u002Fdeployment","fr\u002Fdocs\u002F6.infra\u002F3.deployment",{"title":166,"path":167,"stem":168},"Hôtes","\u002Ffr\u002Fdocs\u002Finfra\u002Fhosts","fr\u002Fdocs\u002F6.infra\u002F4.hosts",{"title":170,"path":171,"stem":172},"Les sources","\u002Ffr\u002Fdocs\u002Finfra\u002Fsources","fr\u002Fdocs\u002F6.infra\u002F5.sources",{"title":174,"path":175,"stem":176},"Le cycle de vie","\u002Ffr\u002Fdocs\u002Finfra\u002Flifecycle","fr\u002Fdocs\u002F6.infra\u002F6.lifecycle",{"title":178,"path":179,"stem":180},"L'identité entre Fronds","\u002Ffr\u002Fdocs\u002Finfra\u002Fidentity","fr\u002Fdocs\u002F6.infra\u002F7.identity",{"title":182,"path":183,"stem":184},"Observabilité","\u002Ffr\u002Fdocs\u002Finfra\u002Fobservability","fr\u002Fdocs\u002F6.infra\u002F8.observability",{"title":186,"path":187,"stem":188},"Tests","\u002Ffr\u002Fdocs\u002Finfra\u002Ftesting","fr\u002Fdocs\u002F6.infra\u002F9.testing",{"id":190,"title":75,"body":191,"description":582,"extension":583,"meta":584,"navigation":585,"path":76,"seo":586,"stem":77,"__hash__":587},"docs_fr\u002Ffr\u002Fdocs\u002F3.schema\u002F5.evolution.md",{"type":192,"value":193,"toc":575},"minimark",[194,198,212,266,271,279,290,305,309,320,326,337,341,352,428,435,442,510,517,521,537,544,548,554,565,571],[195,196,75],"h1",{"id":197},"comment-un-schéma-bouge",[199,200,201,202,206,207,211],"p",{},"Une entité se décrit telle qu'elle est aujourd'hui. ",[203,204,205],"code",{},"fougere freeze"," enregistre ce qu'elle\n",[208,209,210],"strong",{},"était",", si bien que la différence entre deux moments devient une valeur sur laquelle\nquelque chose peut agir.",[213,214,219],"pre",{"className":215,"code":216,"language":217,"meta":218,"style":218},"language-bash shiki shiki-themes material-theme-lighter github-light github-dark","fougere freeze v1        # fronds\u002Fblog\u002Fversions\u002Fv1\u002Fshape.json\nfougere migrate          # ce que la base n'a pas rattrapé\nfougere migrate --apply  # et on y va\n","bash","",[203,220,221,241,252],{"__ignoreMap":218},[222,223,226,230,234,237],"span",{"class":224,"line":225},"line",1,[222,227,229],{"class":228},"sbgvK","fougere",[222,231,233],{"class":232},"s_sjI"," freeze",[222,235,236],{"class":232}," v1",[222,238,240],{"class":239},"sutJx","        # fronds\u002Fblog\u002Fversions\u002Fv1\u002Fshape.json\n",[222,242,244,246,249],{"class":224,"line":243},2,[222,245,229],{"class":228},[222,247,248],{"class":232}," migrate",[222,250,251],{"class":239},"          # ce que la base n'a pas rattrapé\n",[222,253,255,257,259,263],{"class":224,"line":254},3,[222,256,229],{"class":228},[222,258,248],{"class":232},[222,260,262],{"class":261},"stzsN"," --apply",[222,264,265],{"class":239},"  # et on y va\n",[267,268,270],"h2",{"id":269},"un-artefact-plusieurs-lecteurs","Un artefact, plusieurs lecteurs",[213,272,277],{"className":273,"code":275,"language":276},[274],"language-text","fronds\u002Fblog\u002Fversions\u002F\n  v1\u002F  shape.json                  ce que les formes ÉTAIENT\n  v2\u002F  shape.json  from.json       …et le pas qui mène ici\n","text",[203,278,275],{"__ignoreMap":218},[199,280,281,282,285,286,289],{},"Les deux sont écrits, et aucun ne remplace l'autre. ",[208,283,284],{},"Un instantané seul perd l'intention"," —\nun champ parti plus un champ apparu ne se distingue pas d'un renommage. ",[208,287,288],{},"Un pas seul se\ncorrompt en silence"," — rien ne le confronte à quoi que ce soit. Ensemble ils portent un\ninvariant : rejouer le pas sur l'instantané précédent doit redonner celui-ci.",[199,291,292,293,296,297,300,301,304],{},"Une migration lit le pas ",[208,294,295],{},"une fois",", contre les lignes. Le second lecteur est la raison\npour laquelle le pas est un artefact et non un appel de fonction : une ancienne version d'API\nencore servie rejouerait le même pas ",[208,298,299],{},"à chaque appel",", à la porte. ",[208,302,303],{},"Ce lecteur n'est pas\nencore écrit"," — et c'est pourquoi la comparaison vit à un seul endroit, car l'écrire deux\nfois laisserait les deux diverger sur les deux mêmes formes.",[267,306,308],{"id":307},"un-renommage-se-déclare-il-ne-se-devine-jamais","Un renommage se déclare, il ne se devine jamais",[199,310,311,312,315,316,319],{},"Fougere ne décidera pas que ",[203,313,314],{},"body"," est devenu ",[203,317,318],{},"content",", même quand c'est la seule\npossibilité :",[213,321,324],{"className":322,"code":323,"language":276},[274],"◆  post.body — what happened to it?\n   ❯ renamed to content    la donnée le suit\n     dropped               la colonne part, et ce qu'elle tenait avec elle\n",[203,325,323],{"__ignoreMap":218},[199,327,328,329,332,333,336],{},"Les deux lectures produisent des DDL ",[208,330,331],{},"opposées"," — l'une garde une colonne de données\nvivantes, l'autre la jette. L'intention vit dans le moment où le changement a été fait, et\naucune paire d'instantanés ne la contient. C'est le seul endroit où ",[203,334,335],{},"freeze"," pose une\nquestion, et rien n'atteint le disque tant qu'elle n'est pas réglée.",[267,338,340],{"id":339},"ce-quune-migration-réalise","Ce qu'une migration réalise",[199,342,343,344,347,348,351],{},"Une passe automatique est additive à dessein : elle crée une table manquante, ajoute une\ncolonne manquante, et ",[208,345,346],{},"rien d'autre n'arrive jamais",". ",[203,349,350],{},"fougere migrate"," est l'autre moitié —\net seulement ce que le pas dit :",[353,354,355,368],"table",{},[356,357,358],"thead",{},[359,360,361,365],"tr",{},[362,363,364],"th",{},"Le pas dit",[362,366,367],{},"La base fait",[369,370,371,385,397,408,416],"tbody",{},[359,372,373,379],{},[374,375,376],"td",{},[203,377,378],{},"renamed",[374,380,381,384],{},[203,382,383],{},"ALTER TABLE … RENAME COLUMN"," — la donnée suit",[359,386,387,392],{},[374,388,389],{},[203,390,391],{},"removed",[374,393,394],{},[203,395,396],{},"DROP COLUMN",[359,398,399,405],{},[374,400,401,404],{},[203,402,403],{},"added",", optionnel",[374,406,407],{},"rien ; la passe additive s'en charge déjà",[359,409,410,413],{},[374,411,412],{},"un index qui apparaît",[374,414,415],{},"rien ; chaque démarrage propose tous les index déclarés",[359,417,418,425],{},[374,419,420,421,424],{},"une ",[203,422,423],{},"boundary"," qui bouge",[374,426,427],{},"rien — qui peut lire un champ n'est pas dans une colonne",[199,429,430,431,434],{},"La comparaison lit les ",[208,432,433],{},"quatre axes",", pas seulement la forme : un index, un groupe unique,\nune relation, un lifecycle et une boundary sont des différences aussi, et un pas qui ne\nrapportait que la forme répondait au quart de la question.",[199,436,437,438,441],{},"Tout le reste est refusé ",[208,439,440],{},"en le nommant",", et en une fois plutôt qu'un à la fois :",[443,444,445,460,466,472,482,488,494,504],"ul",{},[446,447,448,451,452,455,456,459],"li",{},[208,449,450],{},"un champ requis sans défaut"," — les lignes existantes n'ont rien à mettre dedans.\nDéclarez ",[203,453,454],{},"text({ default: … })",", la même réponse qu'une migration donne quand on ajoute une\ncolonne ",[203,457,458],{},"NOT NULL"," à une table qui a déjà des lignes.",[446,461,462,465],{},[208,463,464],{},"un champ devenu requis"," — même raison, dans l'autre sens.",[446,467,468,471],{},[208,469,470],{},"un type qui bouge"," — aucune conversion ne se dérive de deux formes.",[446,473,474,477,478,481],{},[208,475,476],{},"une borne resserrée"," — la porte la fait respecter ; la table garde son ancien ",[203,479,480],{},"CHECK","\njusqu'à ce que vous la migriez vous-même.",[446,483,484,487],{},[208,485,486],{},"un groupe unique qui bouge"," — les lignes déjà stockées peuvent le contredire, donc\najouter la contrainte est une décision sur les lignes, pas sur le DDL.",[446,489,490,493],{},[208,491,492],{},"une relation qui bouge"," — une clé étrangère est une contrainte, et rien ici n'en altère.",[446,495,496,499,500,503],{},[208,497,498],{},"un défaut qui bouge"," — la table garde l'ancien ; rien ici n'altère un ",[203,501,502],{},"DEFAULT",".",[446,505,506,509],{},[208,507,508],{},"un index qui disparaît"," — rien ne supprime un index aujourd'hui, la table le garde.",[199,511,512,513,516],{},"Un plan qui porte un seul refus n'exécute ",[208,514,515],{},"rien",". Un renommage à moitié fait est pire que\npas fait : la donnée finit dans une colonne dont plus rien ne connaît le nom.",[267,518,520],{"id":519},"rejouer-ne-coûte-rien","Rejouer ne coûte rien",[199,522,523,524,527,528,532,533,536],{},"Lancez ",[203,525,526],{},"migrate"," deux fois et le second passage répond ",[529,530,531],"em",{},"up to date",". Il n'y a aucun registre\ndes migrations appliquées — un changement est sauté parce que ",[208,534,535],{},"les colonnes elles-mêmes","\ndisent qu'il a eu lieu : un renommage dont l'ancien nom a disparu et dont le nouveau est là\na été fait.",[199,538,539,540,543],{},"C'est pour ça que la chaîne est lue entière et ",[208,541,542],{},"composée"," en un seul pas plutôt que rejouée\npas à pas : un champ renommé deux fois est suivi jusqu'au nom sur lequel il finit, le seul\ndont les tables puissent répondre. Un nom intermédiaire qu'elles n'ont jamais porté n'est pas\nune question qu'on peut leur poser. Rien n'a besoin de\nsavoir à quelle version la base est assise, et une colonne que quelqu'un a renommée à la main\nest vue plutôt que contredite. Un registre serait un second enregistrement d'un fait que les\ncolonnes portent déjà, et les deux divergeraient le jour où ils ont divergé.",[267,545,547],{"id":546},"ce-que-ceci-ne-fait-pas","Ce que ceci ne fait pas",[199,549,550,553],{},[208,551,552],{},"Rien ne tourne au démarrage."," Supprimer et renommer touchent des données vivantes : c'est\nune commande que vous lancez, après avoir lu le plan. La passe additive reste où elle était.",[199,555,556,559,560,564],{},[208,557,558],{},"Un gel n'est pas un artefact de build."," Il est committé, parce qu'il enregistre un passé\nqu'aucun code présent ne sait régénérer — contrairement à ",[561,562,563],"a",{"href":68},"la carte\nd'identité",", dérivée des entités d'aujourd'hui et redérivable à tout\nmoment.",[199,566,567,568,570],{},"Ensuite : ",[561,569,67],{"href":68}," — la même idée à travers les process\nplutôt qu'à travers le temps.",[572,573,574],"style",{},"html pre.shiki code .sbgvK, html code.shiki .sbgvK{--shiki-light:#E2931D;--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .s_sjI, html code.shiki .s_sjI{--shiki-light:#91B859;--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .sutJx, html code.shiki .sutJx{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#6A737D;--shiki-default-font-style:inherit;--shiki-dark:#6A737D;--shiki-dark-font-style:inherit}html pre.shiki code .stzsN, html code.shiki .stzsN{--shiki-light:#91B859;--shiki-default:#005CC5;--shiki-dark:#79B8FF}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":218,"searchDepth":243,"depth":243,"links":576},[577,578,579,580,581],{"id":269,"depth":243,"text":270},{"id":307,"depth":243,"text":308},{"id":339,"depth":243,"text":340},{"id":519,"depth":243,"text":520},{"id":546,"depth":243,"text":547},"Geler ce qu'une forme était, le pas qui en part, et la migration qui le réalise — un artefact, plusieurs lecteurs.","md",{},true,{"title":75,"description":582},"3dk_qIH5Izn1JZTYiIA6ezLLZ8GwHZyLIXfArEWNh8A",1788598419698]