[{"data":1,"prerenderedAt":1108},["ShallowReactive",2],{"writes-\u002Fwrites\u002Fdeclarative-isnt-about-abstraction":3},{"id":4,"title":5,"body":6,"date":1097,"description":1098,"extension":1099,"meta":1100,"navigation":125,"path":1101,"seo":1102,"stem":1103,"tags":1104,"__hash__":1107},"writes\u002Fwrites\u002Fdeclarative-isnt-about-abstraction.md","Declarative Isn't About Abstraction",{"type":7,"value":8,"toc":1091},"minimark",[9,26,209,217,220,225,237,240,244,254,552,573,576,579,775,791,795,798,805,808,1055,1062,1069,1073,1078,1081,1084,1087],[10,11,12,13,17,18,21,22,25],"p",{},"A lot of people learn \"declarative\" from the same example. A ",[14,15,16],"code",{},"for"," loop that builds up a sum,\nnext to a ",[14,19,20],{},"filter"," and ",[14,23,24],{},"reduce"," that do the same thing in one line:",[27,28,33],"pre",{"className":29,"code":30,"language":31,"meta":32,"style":32},"language-ts shiki shiki-themes github-light github-dark","\u002F\u002F imperative: the sum is built up, step by step\nlet evenSum = 0;\nfor (const n of numbers) {\n  if (n % 2 === 0) evenSum += n;\n}\n\n\u002F\u002F declarative: the sum is one expression\nconst evenSum = numbers.filter((n) => n % 2 === 0).reduce((a, b) => a + b, 0);\n","ts","",[14,34,35,44,65,85,114,120,127,133],{"__ignoreMap":32},[36,37,40],"span",{"class":38,"line":39},"line",1,[36,41,43],{"class":42},"sJ8bj","\u002F\u002F imperative: the sum is built up, step by step\n",[36,45,47,51,55,58,62],{"class":38,"line":46},2,[36,48,50],{"class":49},"szBVR","let",[36,52,54],{"class":53},"sVt8B"," evenSum ",[36,56,57],{"class":49},"=",[36,59,61],{"class":60},"sj4cs"," 0",[36,63,64],{"class":53},";\n",[36,66,68,70,73,76,79,82],{"class":38,"line":67},3,[36,69,16],{"class":49},[36,71,72],{"class":53}," (",[36,74,75],{"class":49},"const",[36,77,78],{"class":60}," n",[36,80,81],{"class":49}," of",[36,83,84],{"class":53}," numbers) {\n",[36,86,88,91,94,97,100,103,105,108,111],{"class":38,"line":87},4,[36,89,90],{"class":49},"  if",[36,92,93],{"class":53}," (n ",[36,95,96],{"class":49},"%",[36,98,99],{"class":60}," 2",[36,101,102],{"class":49}," ===",[36,104,61],{"class":60},[36,106,107],{"class":53},") evenSum ",[36,109,110],{"class":49},"+=",[36,112,113],{"class":53}," n;\n",[36,115,117],{"class":38,"line":116},5,[36,118,119],{"class":53},"}\n",[36,121,123],{"class":38,"line":122},6,[36,124,126],{"emptyLinePlaceholder":125},true,"\n",[36,128,130],{"class":38,"line":129},7,[36,131,132],{"class":42},"\u002F\u002F declarative: the sum is one expression\n",[36,134,136,138,141,144,147,150,153,157,160,163,166,168,170,172,174,177,179,181,184,187,190,192,194,197,200,203,206],{"class":38,"line":135},8,[36,137,75],{"class":49},[36,139,140],{"class":60}," evenSum",[36,142,143],{"class":49}," =",[36,145,146],{"class":53}," numbers.",[36,148,20],{"class":149},"sScJk",[36,151,152],{"class":53},"((",[36,154,156],{"class":155},"s4XuR","n",[36,158,159],{"class":53},") ",[36,161,162],{"class":49},"=>",[36,164,165],{"class":53}," n ",[36,167,96],{"class":49},[36,169,99],{"class":60},[36,171,102],{"class":49},[36,173,61],{"class":60},[36,175,176],{"class":53},").",[36,178,24],{"class":149},[36,180,152],{"class":53},[36,182,183],{"class":155},"a",[36,185,186],{"class":53},", ",[36,188,189],{"class":155},"b",[36,191,159],{"class":53},[36,193,162],{"class":49},[36,195,196],{"class":53}," a ",[36,198,199],{"class":49},"+",[36,201,202],{"class":53}," b, ",[36,204,205],{"class":60},"0",[36,207,208],{"class":53},");\n",[10,210,211,212,216],{},"This example isn't wrong. But I feel it's just not enough to actually understand declarative, and\non its own it quietly teaches the wrong lesson: ",[213,214,215],"em",{},"declarative means less code and more\nabstraction."," So people go looking for things to abstract. They wrap everything in helper\nfunctions, build little pipelines, and pat themselves on the back for writing declarative code.",[10,218,219],{},"But that's not what declarative means. You can write a mountain of helpers\u002Fabstractions and\nstill be completely imperative.",[221,222,224],"h2",{"id":223},"the-definition-i-actually-use","The definition I actually use",[10,226,227,228,232,233,236],{},"Declarative code describes a thing in ",[229,230,231],"strong",{},"one place",". Imperative code ",[229,234,235],{},"scatters"," that\ndescription, the thing is declared here, and then changed somewhere else.",[10,238,239],{},"That's the whole distinction for me. It's not only \"what vs how,\" or \"high level vs low level.\"\nIt's a mindset shift: when I want to know what a value is, can I look in one place and follow the\ndependency chain, or do I have to trace every place that touches it?",[221,241,243],{"id":242},"abstraction-is-a-different-axis","Abstraction is a different axis",[10,245,246,247,250,251,253],{},"Here's a cart. The ",[14,248,249],{},"total"," is derived from the items and a discount. Watch where ",[14,252,249],{}," lives:",[27,255,257],{"className":29,"code":256,"language":31,"meta":32,"style":32},"class Cart {\n  private items: Item[] = [];\n  private discount = 0;\n  total = 0;\n\n  addItem(item: Item) {\n    this.items.push(item);\n    this.recalculateTotal();\n  }\n\n  removeItem(id: string) {\n    this.items = this.items.filter((i) => i.id !== id);\n    this.recalculateTotal();\n  }\n\n  applyDiscount(amount: number) {\n    this.discount = amount;\n    this.recalculateTotal();\n  }\n\n  private recalculateTotal() {\n    this.total = sumItems(this.items) - this.discount;\n  }\n}\n",[14,258,259,270,292,305,316,320,338,352,365,371,376,394,429,440,445,450,468,481,492,497,502,513,542,547],{"__ignoreMap":32},[36,260,261,264,267],{"class":38,"line":39},[36,262,263],{"class":49},"class",[36,265,266],{"class":149}," Cart",[36,268,269],{"class":53}," {\n",[36,271,272,275,278,281,284,287,289],{"class":38,"line":46},[36,273,274],{"class":49},"  private",[36,276,277],{"class":155}," items",[36,279,280],{"class":49},":",[36,282,283],{"class":149}," Item",[36,285,286],{"class":53},"[] ",[36,288,57],{"class":49},[36,290,291],{"class":53}," [];\n",[36,293,294,296,299,301,303],{"class":38,"line":67},[36,295,274],{"class":49},[36,297,298],{"class":155}," discount",[36,300,143],{"class":49},[36,302,61],{"class":60},[36,304,64],{"class":53},[36,306,307,310,312,314],{"class":38,"line":87},[36,308,309],{"class":155},"  total",[36,311,143],{"class":49},[36,313,61],{"class":60},[36,315,64],{"class":53},[36,317,318],{"class":38,"line":116},[36,319,126],{"emptyLinePlaceholder":125},[36,321,322,325,328,331,333,335],{"class":38,"line":122},[36,323,324],{"class":149},"  addItem",[36,326,327],{"class":53},"(",[36,329,330],{"class":155},"item",[36,332,280],{"class":49},[36,334,283],{"class":149},[36,336,337],{"class":53},") {\n",[36,339,340,343,346,349],{"class":38,"line":129},[36,341,342],{"class":60},"    this",[36,344,345],{"class":53},".items.",[36,347,348],{"class":149},"push",[36,350,351],{"class":53},"(item);\n",[36,353,354,356,359,362],{"class":38,"line":135},[36,355,342],{"class":60},[36,357,358],{"class":53},".",[36,360,361],{"class":149},"recalculateTotal",[36,363,364],{"class":53},"();\n",[36,366,368],{"class":38,"line":367},9,[36,369,370],{"class":53},"  }\n",[36,372,374],{"class":38,"line":373},10,[36,375,126],{"emptyLinePlaceholder":125},[36,377,379,382,384,387,389,392],{"class":38,"line":378},11,[36,380,381],{"class":149},"  removeItem",[36,383,327],{"class":53},[36,385,386],{"class":155},"id",[36,388,280],{"class":49},[36,390,391],{"class":60}," string",[36,393,337],{"class":53},[36,395,397,399,402,404,407,409,411,413,416,418,420,423,426],{"class":38,"line":396},12,[36,398,342],{"class":60},[36,400,401],{"class":53},".items ",[36,403,57],{"class":49},[36,405,406],{"class":60}," this",[36,408,345],{"class":53},[36,410,20],{"class":149},[36,412,152],{"class":53},[36,414,415],{"class":155},"i",[36,417,159],{"class":53},[36,419,162],{"class":49},[36,421,422],{"class":53}," i.id ",[36,424,425],{"class":49},"!==",[36,427,428],{"class":53}," id);\n",[36,430,432,434,436,438],{"class":38,"line":431},13,[36,433,342],{"class":60},[36,435,358],{"class":53},[36,437,361],{"class":149},[36,439,364],{"class":53},[36,441,443],{"class":38,"line":442},14,[36,444,370],{"class":53},[36,446,448],{"class":38,"line":447},15,[36,449,126],{"emptyLinePlaceholder":125},[36,451,453,456,458,461,463,466],{"class":38,"line":452},16,[36,454,455],{"class":149},"  applyDiscount",[36,457,327],{"class":53},[36,459,460],{"class":155},"amount",[36,462,280],{"class":49},[36,464,465],{"class":60}," number",[36,467,337],{"class":53},[36,469,471,473,476,478],{"class":38,"line":470},17,[36,472,342],{"class":60},[36,474,475],{"class":53},".discount ",[36,477,57],{"class":49},[36,479,480],{"class":53}," amount;\n",[36,482,484,486,488,490],{"class":38,"line":483},18,[36,485,342],{"class":60},[36,487,358],{"class":53},[36,489,361],{"class":149},[36,491,364],{"class":53},[36,493,495],{"class":38,"line":494},19,[36,496,370],{"class":53},[36,498,500],{"class":38,"line":499},20,[36,501,126],{"emptyLinePlaceholder":125},[36,503,505,507,510],{"class":38,"line":504},21,[36,506,274],{"class":49},[36,508,509],{"class":149}," recalculateTotal",[36,511,512],{"class":53},"() {\n",[36,514,516,518,521,523,526,528,531,534,537,539],{"class":38,"line":515},22,[36,517,342],{"class":60},[36,519,520],{"class":53},".total ",[36,522,57],{"class":49},[36,524,525],{"class":149}," sumItems",[36,527,327],{"class":53},[36,529,530],{"class":60},"this",[36,532,533],{"class":53},".items) ",[36,535,536],{"class":49},"-",[36,538,406],{"class":60},[36,540,541],{"class":53},".discount;\n",[36,543,545],{"class":38,"line":544},23,[36,546,370],{"class":53},[36,548,550],{"class":38,"line":549},24,[36,551,119],{"class":53},[10,553,554,555,557,558,560,561,563,564,569,570,572],{},"This looks clean. The calculation is pulled into one tidy ",[14,556,361],{}," method that's\nabstraction, and it's fine. But ",[14,559,249],{}," is still imperative. Its real value isn't defined by\n",[14,562,249],{},", it's defined by ",[213,565,566,567],{},"every method that remembers to call ",[14,568,361],{},". Forget one,\nand ",[14,571,249],{}," is wrong. The description is split across three methods and held together by a\nrule you have to keep in your head.",[10,574,575],{},"No amount of extra helpers fixes that. I could extract ten more functions and the problem would\nbe exactly the same. Abstraction is about how much detail you hide. Declarative is about whether\nthe description is in one place. They are different axes.",[10,577,578],{},"The declarative version doesn't need more abstraction. It needs less scattering:",[27,580,582],{"className":29,"code":581,"language":31,"meta":32,"style":32},"class Cart {\n  private items: Item[] = [];\n  private discount = 0;\n\n  get total() {\n    return sumItems(this.items) - this.discount;\n  }\n\n  addItem(item: Item) {\n    this.items.push(item);\n  }\n\n  removeItem(id: string) {\n    this.items = this.items.filter((i) => i.id !== id);\n  }\n\n  applyDiscount(amount: number) {\n    this.discount = amount;\n  }\n}\n",[14,583,584,592,608,620,624,634,653,657,661,675,685,689,693,707,735,739,743,757,767,771],{"__ignoreMap":32},[36,585,586,588,590],{"class":38,"line":39},[36,587,263],{"class":49},[36,589,266],{"class":149},[36,591,269],{"class":53},[36,593,594,596,598,600,602,604,606],{"class":38,"line":46},[36,595,274],{"class":49},[36,597,277],{"class":155},[36,599,280],{"class":49},[36,601,283],{"class":149},[36,603,286],{"class":53},[36,605,57],{"class":49},[36,607,291],{"class":53},[36,609,610,612,614,616,618],{"class":38,"line":67},[36,611,274],{"class":49},[36,613,298],{"class":155},[36,615,143],{"class":49},[36,617,61],{"class":60},[36,619,64],{"class":53},[36,621,622],{"class":38,"line":87},[36,623,126],{"emptyLinePlaceholder":125},[36,625,626,629,632],{"class":38,"line":116},[36,627,628],{"class":49},"  get",[36,630,631],{"class":149}," total",[36,633,512],{"class":53},[36,635,636,639,641,643,645,647,649,651],{"class":38,"line":122},[36,637,638],{"class":49},"    return",[36,640,525],{"class":149},[36,642,327],{"class":53},[36,644,530],{"class":60},[36,646,533],{"class":53},[36,648,536],{"class":49},[36,650,406],{"class":60},[36,652,541],{"class":53},[36,654,655],{"class":38,"line":129},[36,656,370],{"class":53},[36,658,659],{"class":38,"line":135},[36,660,126],{"emptyLinePlaceholder":125},[36,662,663,665,667,669,671,673],{"class":38,"line":367},[36,664,324],{"class":149},[36,666,327],{"class":53},[36,668,330],{"class":155},[36,670,280],{"class":49},[36,672,283],{"class":149},[36,674,337],{"class":53},[36,676,677,679,681,683],{"class":38,"line":373},[36,678,342],{"class":60},[36,680,345],{"class":53},[36,682,348],{"class":149},[36,684,351],{"class":53},[36,686,687],{"class":38,"line":378},[36,688,370],{"class":53},[36,690,691],{"class":38,"line":396},[36,692,126],{"emptyLinePlaceholder":125},[36,694,695,697,699,701,703,705],{"class":38,"line":431},[36,696,381],{"class":149},[36,698,327],{"class":53},[36,700,386],{"class":155},[36,702,280],{"class":49},[36,704,391],{"class":60},[36,706,337],{"class":53},[36,708,709,711,713,715,717,719,721,723,725,727,729,731,733],{"class":38,"line":442},[36,710,342],{"class":60},[36,712,401],{"class":53},[36,714,57],{"class":49},[36,716,406],{"class":60},[36,718,345],{"class":53},[36,720,20],{"class":149},[36,722,152],{"class":53},[36,724,415],{"class":155},[36,726,159],{"class":53},[36,728,162],{"class":49},[36,730,422],{"class":53},[36,732,425],{"class":49},[36,734,428],{"class":53},[36,736,737],{"class":38,"line":447},[36,738,370],{"class":53},[36,740,741],{"class":38,"line":452},[36,742,126],{"emptyLinePlaceholder":125},[36,744,745,747,749,751,753,755],{"class":38,"line":470},[36,746,455],{"class":149},[36,748,327],{"class":53},[36,750,460],{"class":155},[36,752,280],{"class":49},[36,754,465],{"class":60},[36,756,337],{"class":53},[36,758,759,761,763,765],{"class":38,"line":483},[36,760,342],{"class":60},[36,762,475],{"class":53},[36,764,57],{"class":49},[36,766,480],{"class":53},[36,768,769],{"class":38,"line":494},[36,770,370],{"class":53},[36,772,773],{"class":38,"line":499},[36,774,119],{"class":53},[10,776,777,779,780,782,783,786,787,790],{},[14,778,249],{}," is described once, right where it lives, in terms of what it actually depends on. The\nmutators only touch the facts they own. There's no ",[14,781,361],{}," to forget, because there's\nnothing to keep in sync. This version has ",[213,784,785],{},"fewer"," moving parts and ",[213,788,789],{},"less"," abstraction than the\nfirst one, and it's the declarative one. That's the part the loop-vs-filter example never tells you.",[221,792,794],{"id":793},"derived-values-that-depend-on-each-other","Derived values that depend on each other",[10,796,797],{},"The cart above had just one derived value, easy to keep in sync by hand, and deriving it barely\nfeels worth the trouble. The payoff shows up when values depend on other derived values. A\nreal checkout isn't just a total: it's a subtotal, then a total after discount, then tax on that\ntotal, then a grand total with shipping. Each one feeds the next.",[10,799,800,801,804],{},"Do that imperatively and every input change has to walk the whole chain, in the right order,\nwithout missing a step: change the discount, recompute the total, then the tax, then the grand\ntotal. Miss one, or run them out of order, and the numbers are quietly wrong. The description of\n",[14,802,803],{},"grandTotal"," is now smeared across every method that touches anything upstream of it.",[10,806,807],{},"Any modern framework lets you write that same chain declaratively instead. Here it is with\nAngular signals:",[27,809,811],{"className":29,"code":810,"language":31,"meta":32,"style":32},"@Component({\n  template: `\u003Cp>Due today: {{ grandTotal() }}\u003C\u002Fp>`,\n})\nclass CheckoutComponent {\n  private items = signal\u003CItem[]>([]);\n  private discount = signal(0);\n  private shipping = signal(0);\n\n  private subtotal = computed(() => sumItems(this.items()));\n  private total = computed(() => this.subtotal() - this.discount());\n  private tax = computed(() => this.total() * TAX_RATE);\n  readonly grandTotal = computed(() => this.total() + this.tax() + this.shipping());\n}\n",[14,812,813,824,836,841,850,870,886,903,907,938,974,1005,1051],{"__ignoreMap":32},[36,814,815,818,821],{"class":38,"line":39},[36,816,817],{"class":53},"@",[36,819,820],{"class":149},"Component",[36,822,823],{"class":53},"({\n",[36,825,826,829,833],{"class":38,"line":46},[36,827,828],{"class":53},"  template: ",[36,830,832],{"class":831},"sZZnC","`\u003Cp>Due today: {{ grandTotal() }}\u003C\u002Fp>`",[36,834,835],{"class":53},",\n",[36,837,838],{"class":38,"line":67},[36,839,840],{"class":53},"})\n",[36,842,843,845,848],{"class":38,"line":87},[36,844,263],{"class":49},[36,846,847],{"class":149}," CheckoutComponent",[36,849,269],{"class":53},[36,851,852,854,856,858,861,864,867],{"class":38,"line":116},[36,853,274],{"class":49},[36,855,277],{"class":155},[36,857,143],{"class":49},[36,859,860],{"class":149}," signal",[36,862,863],{"class":53},"\u003C",[36,865,866],{"class":149},"Item",[36,868,869],{"class":53},"[]>([]);\n",[36,871,872,874,876,878,880,882,884],{"class":38,"line":122},[36,873,274],{"class":49},[36,875,298],{"class":155},[36,877,143],{"class":49},[36,879,860],{"class":149},[36,881,327],{"class":53},[36,883,205],{"class":60},[36,885,208],{"class":53},[36,887,888,890,893,895,897,899,901],{"class":38,"line":129},[36,889,274],{"class":49},[36,891,892],{"class":155}," shipping",[36,894,143],{"class":49},[36,896,860],{"class":149},[36,898,327],{"class":53},[36,900,205],{"class":60},[36,902,208],{"class":53},[36,904,905],{"class":38,"line":135},[36,906,126],{"emptyLinePlaceholder":125},[36,908,909,911,914,916,919,922,924,926,928,930,932,935],{"class":38,"line":367},[36,910,274],{"class":49},[36,912,913],{"class":155}," subtotal",[36,915,143],{"class":49},[36,917,918],{"class":149}," computed",[36,920,921],{"class":53},"(() ",[36,923,162],{"class":49},[36,925,525],{"class":149},[36,927,327],{"class":53},[36,929,530],{"class":60},[36,931,358],{"class":53},[36,933,934],{"class":149},"items",[36,936,937],{"class":53},"()));\n",[36,939,940,942,944,946,948,950,952,954,956,959,962,964,966,968,971],{"class":38,"line":373},[36,941,274],{"class":49},[36,943,631],{"class":155},[36,945,143],{"class":49},[36,947,918],{"class":149},[36,949,921],{"class":53},[36,951,162],{"class":49},[36,953,406],{"class":60},[36,955,358],{"class":53},[36,957,958],{"class":149},"subtotal",[36,960,961],{"class":53},"() ",[36,963,536],{"class":49},[36,965,406],{"class":60},[36,967,358],{"class":53},[36,969,970],{"class":149},"discount",[36,972,973],{"class":53},"());\n",[36,975,976,978,981,983,985,987,989,991,993,995,997,1000,1003],{"class":38,"line":378},[36,977,274],{"class":49},[36,979,980],{"class":155}," tax",[36,982,143],{"class":49},[36,984,918],{"class":149},[36,986,921],{"class":53},[36,988,162],{"class":49},[36,990,406],{"class":60},[36,992,358],{"class":53},[36,994,249],{"class":149},[36,996,961],{"class":53},[36,998,999],{"class":49},"*",[36,1001,1002],{"class":60}," TAX_RATE",[36,1004,208],{"class":53},[36,1006,1007,1010,1013,1015,1017,1019,1021,1023,1025,1027,1029,1031,1033,1035,1038,1040,1042,1044,1046,1049],{"class":38,"line":396},[36,1008,1009],{"class":49},"  readonly",[36,1011,1012],{"class":155}," grandTotal",[36,1014,143],{"class":49},[36,1016,918],{"class":149},[36,1018,921],{"class":53},[36,1020,162],{"class":49},[36,1022,406],{"class":60},[36,1024,358],{"class":53},[36,1026,249],{"class":149},[36,1028,961],{"class":53},[36,1030,199],{"class":49},[36,1032,406],{"class":60},[36,1034,358],{"class":53},[36,1036,1037],{"class":149},"tax",[36,1039,961],{"class":53},[36,1041,199],{"class":49},[36,1043,406],{"class":60},[36,1045,358],{"class":53},[36,1047,1048],{"class":149},"shipping",[36,1050,973],{"class":53},[36,1052,1053],{"class":38,"line":431},[36,1054,119],{"class":53},[10,1056,1057,1058,1061],{},"Every value is described once, in terms of the ones below it. There is no recompute order to get\nright, because the dependencies ",[213,1059,1060],{},"are"," the order. Change any input and the whole chain, including\nwhat the template shows, resolves itself. The more the values depend on each other, the more the\nscattering costs you.",[10,1063,1064,1065,1068],{},"Angular signal made that clean, but they aren't what made it declarative. Describing each value\nin one place did. Declarative isn't something a framework ",[213,1066,1067],{},"gives"," you; it's a mental model you\nbring. Every time you decide whether a value describes itself or gets described from the outside,\nthat's the choice that makes code declarative, not the library you chose.",[221,1070,1072],{"id":1071},"the-thinking-outlives-the-typing","The thinking outlives the typing",[10,1074,1075],{},[213,1076,1077],{},"\"I don't write code anymore, the AI does it for me. Why should I care whether it's declarative?\"",[10,1079,1080],{},"Because an AI model is confident even when it's wrong. When you stop writing the code, the work\nshifts from writing it to judging it, and you can't judge what you can't frame. A system reacts\nto input\u002Fevents, so following how the data flows, where each value comes from and what it\nchanges, is the fastest way I know to tell whether it behaves correctly.",[10,1082,1083],{},"The deeper reason is about what the model reads, not what it writes. AI leans on the surrounding\ncode as its source of truth and imitates it. Work with several agents on the same feature and\nthat context drifts fast. If the code already scatters one value across a dozen places, the next\nchange spreads it across a dozen more, and bad code compounds. Keep it declarative and you hand\nthe AI a cleaner map to follow.",[10,1085,1086],{},"The mindset didn't retire when I stopped writing code. It became one thing I bring, and it's what\nkeeps my code easy to predict.",[1088,1089,1090],"style",{},"html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .s4XuR, html code.shiki .s4XuR{--shiki-default:#E36209;--shiki-dark:#FFAB70}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);}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}",{"title":32,"searchDepth":46,"depth":46,"links":1092},[1093,1094,1095,1096],{"id":223,"depth":46,"text":224},{"id":242,"depth":46,"text":243},{"id":793,"depth":46,"text":794},{"id":1071,"depth":46,"text":1072},"2026-08-2","Here's how I actually understand declarative code. It's about where a value is described, not how much code you hide.","md",{},"\u002Fwrites\u002Fdeclarative-isnt-about-abstraction",{"title":5,"description":1098},"writes\u002Fdeclarative-isnt-about-abstraction",[1105,1106],"opinion","fundamentals","zXnUZmsvdKm2-cprmvH0Qj0sI0b7gzE4HNhVVke0mtM",1792318111651]