[{"data":1,"prerenderedAt":636},["ShallowReactive",2],{"insights-list":3},[4,129,280,530],{"id":5,"title":6,"body":7,"category":117,"date":118,"description":119,"draft":120,"extension":121,"meta":122,"navigation":123,"path":124,"readingTime":125,"seo":126,"stem":127,"__hash__":128},"insights\u002Finsights\u002Fprocess-before-features.md","先問流程，再問功能",{"type":8,"value":9,"toc":108},"minimark",[10,14,17,21,27,30,35,38,43,46,49,52,65,68,71,74,77,80,83,86,90,93,102,105],[11,12,13],"p",{},"第一次和企業聊系統的時候，最常拿到的是一份功能清單。上面寫著「要有登入」「要有報表」「要有通知」。清單本身沒有問題，問題是它通常是從別人的系統抄來的，而不是從自己的流程長出來的。",[11,15,16],{},"所以我們現在習慣先把功能清單放在旁邊，改問另一組問題。",[18,19,20],"h2",{"id":20},"先問的三件事",[11,22,23],{},[24,25,26],"strong",{},"這件事現在是誰在做？",[11,28,29],{},"不是「哪個部門」，是哪一個人。找到那個人，通常也就找到了所有的例外狀況。",[11,31,32],{},[24,33,34],{},"他現在用什麼做？",[11,36,37],{},"答案很常是 Excel、LINE、紙本，或是三個一起。這不是落後，而是資訊。它代表現有工具至少在某個地方是好用的，不然不會活這麼久。",[11,39,40],{},[24,41,42],{},"做錯的時候會怎樣？",[11,44,45],{},"有些環節做錯只是重做一次，有些做錯會直接影響出貨或收錢。這個問題的答案，會決定哪裡需要防呆、哪裡可以放寬。",[18,47,48],{"id":48},"功能清單的問題",[11,50,51],{},"功能清單描述的是系統，流程描述的是人。兩者之間往往差了一段沒有人記錄下來的東西：",[53,54,55,59,62],"ul",{},[56,57,58],"li",{},"那個「先傳照片到群組給主管看一下」的步驟",[56,60,61],{},"那個「星期五下午單子會特別多」的節奏",[56,63,64],{},"那個「新人前兩週都會做錯同一件事」的現象",[11,66,67],{},"這些東西不會出現在需求文件裡，但它們決定了系統上線之後會不會被繞過。",[18,69,70],{"id":70},"一個實際的例子",[11,72,73],{},"有一種很常見的需求是「我要一個報表系統」。",[11,75,76],{},"如果直接照做，結果通常是一個有很多圖表的頁面，然後三個月後沒有人打開。",[11,78,79],{},"但如果先問「你做這份報表是為了回答什麼問題」，答案可能是「我只是想知道這週有沒有哪一天特別忙」。",[11,81,82],{},"那麼需要的可能不是報表系統，而是一個每週寄一次、只有五行字的信。",[11,84,85],{},"這兩件事的開發成本差了不只十倍，而後者比較有機會真的被看。",[18,87,89],{"id":88},"流程問清楚之後功能反而好決定","流程問清楚之後，功能反而好決定",[11,91,92],{},"把流程攤開之後，功能清單通常會發生兩件事：",[94,95,96,99],"ol",{},[56,97,98],{},"有一半的功能會被刪掉，因為它們解決的是不存在的問題。",[56,100,101],{},"會冒出兩三個原本沒寫的功能，而且它們才是真正的重點。",[11,103,104],{},"這個過程不需要很久。多數情況下，把一天的實際工作走過一次，大概兩個小時就夠了。",[11,106,107],{},"比起花兩個月做一個沒人用的系統，這兩個小時很便宜。",{"title":109,"searchDepth":110,"depth":110,"links":111},"",3,[112,114,115,116],{"id":20,"depth":113,"text":20},2,{"id":48,"depth":113,"text":48},{"id":70,"depth":113,"text":70},{"id":88,"depth":113,"text":89},"企業數位化","2026-08-12","很多系統做完之後沒人用，不是因為功能不夠，而是因為它從一開始就照著功能清單設計，而不是照著人實際的工作方式設計。",false,"md",{},true,"\u002Finsights\u002Fprocess-before-features",6,{"title":6,"description":119},"insights\u002Fprocess-before-features","7-zsY3oWaYKvoW50feBNsUNNqp91NdKpd-dtowIB1rU",{"id":130,"title":131,"body":132,"category":271,"date":272,"description":273,"draft":120,"extension":121,"meta":274,"navigation":123,"path":275,"readingTime":276,"seo":277,"stem":278,"__hash__":279},"insights\u002Finsights\u002Fpermissions-are-not-a-final-step.md","權限不是上線前才補的東西",{"type":8,"value":133,"toc":264},[134,137,140,143,146,152,158,164,167,170,173,176,230,233,239,242,245,248,251,254,257,260],[11,135,136],{},"在系統開發裡，權限常常被排在很後面。先把畫面做出來、先讓流程跑得動，權限「之後再加」。",[11,138,139],{},"聽起來很合理，但實際上這個順序有一個問題：權限不是一層可以外掛的東西，它會決定資料怎麼查、怎麼存、怎麼分頁。等到功能都做完才回頭處理，通常會發現要改的不是幾行判斷，而是整個資料存取的寫法。",[18,141,142],{"id":142},"三個不同層次的權限",[11,144,145],{},"把權限分開來想，會比較容易決定每一層要做什麼。",[11,147,148,151],{},[24,149,150],{},"介面層","：這個按鈕要不要顯示。這是最表面的一層，也是唯一一層可以被使用者繞過的。",[11,153,154,157],{},[24,155,156],{},"應用層","：這個請求可不可以執行。伺服器收到請求時要自己判斷，不能相信前端已經檢查過。",[11,159,160,163],{},[24,161,162],{},"資料層","：這筆資料可不可以被讀到。資料庫本身也應該有規則，這樣即使應用層有漏洞，也不會直接把整張表倒出去。",[11,165,166],{},"只做第一層，等於沒有做。三層都做，才有辦法在某一層出錯的時候還有東西擋著。",[18,168,169],{"id":169},"從最小權限開始",[11,171,172],{},"比較安全的預設是：一開始什麼都不給，再依角色一項一項加回來。",[11,174,175],{},"反過來做（先全部開放，再逐一關掉）幾乎一定會漏掉東西，而且漏掉的地方通常不會有人回報，因為使用者不會抱怨「我看到了不該看的資料」。",[177,178,182],"pre",{"className":179,"code":180,"language":181,"meta":109,"style":109},"language-sql shiki shiki-themes github-light github-light","-- 不是「這個角色不能看什麼」，\n-- 而是「這個角色只能看什麼」\nselect * from orders\nwhere store_id = current_store_id();\n","sql",[183,184,185,194,199,215],"code",{"__ignoreMap":109},[186,187,190],"span",{"class":188,"line":189},"line",1,[186,191,193],{"class":192},"sJ8bj","-- 不是「這個角色不能看什麼」，\n",[186,195,196],{"class":188,"line":113},[186,197,198],{"class":192},"-- 而是「這個角色只能看什麼」\n",[186,200,201,205,208,211],{"class":188,"line":110},[186,202,204],{"class":203},"sCydW","select",[186,206,207],{"class":203}," *",[186,209,210],{"class":203}," from",[186,212,214],{"class":213},"sKWpL"," orders\n",[186,216,218,221,224,227],{"class":188,"line":217},4,[186,219,220],{"class":203},"where",[186,222,223],{"class":213}," store_id ",[186,225,226],{"class":203},"=",[186,228,229],{"class":213}," current_store_id();\n",[18,231,232],{"id":232},"前端隱藏不是安全機制",[11,234,235,238],{},[183,236,237],{},"v-if=\"isAdmin\""," 只是讓畫面乾淨，不是保護。任何人打開開發者工具都可以看到前端拿到了哪些資料。",[11,240,241],{},"判斷方式很簡單：如果把這個請求直接用 curl 打一次，會發生什麼事？如果答案是「會拿到資料」，那這個地方就沒有真的被保護。",[18,243,244],{"id":244},"環境也是權限的一部分",[11,246,247],{},"正式環境的金鑰不應該出現在開發環境，開發用的測試帳號也不應該出現在正式資料庫。這兩件事聽起來很基本，但在時間趕的時候特別容易被跳過。",[11,249,250],{},"比較實際的做法是一開始就把環境變數分好，讓「拿錯金鑰」這件事在流程上就做不到，而不是靠記得。",[18,252,253],{"id":253},"什麼時候處理",[11,255,256],{},"我們的習慣是在畫資料表的時候就一起決定：這張表有哪些角色會碰到、各自能做什麼。",[11,258,259],{},"這個階段做這件事幾乎不花時間，因為還沒有任何程式碼需要改。等到上線前一週才想，成本就完全不一樣了。",[261,262,263],"style",{},"html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}html pre.shiki code .sCydW, html code.shiki .sCydW{--shiki-default:#D73A49;--shiki-dark:#D73A49}html pre.shiki code .sKWpL, html code.shiki .sKWpL{--shiki-default:#24292E;--shiki-dark:#24292E}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":109,"searchDepth":110,"depth":110,"links":265},[266,267,268,269,270],{"id":142,"depth":113,"text":142},{"id":169,"depth":113,"text":169},{"id":232,"depth":113,"text":232},{"id":244,"depth":113,"text":244},{"id":253,"depth":113,"text":253},"資安","2026-08-05","「先把功能做出來，權限之後再說」是很常見的順序，但它幾乎一定會讓你在最後一週重寫一次資料存取邏輯。",{},"\u002Finsights\u002Fpermissions-are-not-a-final-step",7,{"title":131,"description":273},"insights\u002Fpermissions-are-not-a-final-step","zWtmc13aqFoDwMCfylZRS7XYckT8GRrG34i03fYoBkQ",{"id":281,"title":282,"body":283,"category":522,"date":523,"description":524,"draft":120,"extension":121,"meta":525,"navigation":123,"path":526,"readingTime":396,"seo":527,"stem":528,"__hash__":529},"insights\u002Finsights\u002Fhow-much-javascript-does-a-website-need.md","一個企業網站到底需要多少 JavaScript",{"type":8,"value":284,"toc":515},[285,288,291,294,297,300,303,307,310,321,324,327,330,473,480,483,486,489,500,503,506,509,512],[11,286,287],{},"做企業形象網站的時候，一個很值得先問的問題是：這個網站真正需要「互動」的地方有幾個？",[11,289,290],{},"答案通常是三個以內：導覽選單、捲動進場、還有一兩個展開收合。",[11,292,293],{},"其他部分基本上是文字、圖片與連結。",[18,295,296],{"id":296},"那為什麼還要用框架",[11,298,299],{},"因為維護。同一份導覽列要出現在八個頁面上，用元件寫一次比複製八次好；SEO 的 metadata 要跟著頁面走，用框架處理比手動維護八份 HTML 好。",[11,301,302],{},"框架的價值在於開發與維護方式，不在於執行時要送多少 JavaScript 到瀏覽器。這兩件事其實可以分開。",[18,304,306],{"id":305},"靜態產生-少量-hydration","靜態產生 + 少量 hydration",[11,308,309],{},"比較舒服的組合是：",[53,311,312,315,318],{},[56,313,314],{},"頁面在建置階段就產生好 HTML，訪客第一次連進來拿到的就是完整內容",[56,316,317],{},"需要互動的元件才在瀏覽器端接手",[56,319,320],{},"純展示的區塊不需要任何 JavaScript",[11,322,323],{},"這樣做的好處很直接：第一次載入快、SEO 沒有問題、即使 JavaScript 載入失敗，內容仍然看得到。",[18,325,326],{"id":326},"動畫的部分",[11,328,329],{},"大部分進場動畫都可以用 CSS 完成：",[177,331,335],{"className":332,"code":333,"language":334,"meta":109,"style":109},"language-css shiki shiki-themes github-light github-light","@media (prefers-reduced-motion: no-preference) {\n  [data-reveal] {\n    opacity: 0;\n    transform: translateY(18px);\n  }\n  [data-reveal].is-visible {\n    opacity: 1;\n    transform: none;\n    transition: opacity .85s, transform .85s;\n  }\n}\n","css",[183,336,337,345,357,372,394,400,415,426,438,462,467],{"__ignoreMap":109},[186,338,339,342],{"class":188,"line":189},[186,340,341],{"class":203},"@media",[186,343,344],{"class":213}," (prefers-reduced-motion: no-preference) {\n",[186,346,347,350,354],{"class":188,"line":113},[186,348,349],{"class":213},"  [",[186,351,353],{"class":352},"se37E","data-reveal",[186,355,356],{"class":213},"] {\n",[186,358,359,363,366,369],{"class":188,"line":110},[186,360,362],{"class":361},"sMN4m","    opacity",[186,364,365],{"class":213},": ",[186,367,368],{"class":361},"0",[186,370,371],{"class":213},";\n",[186,373,374,377,379,382,385,388,391],{"class":188,"line":217},[186,375,376],{"class":361},"    transform",[186,378,365],{"class":213},[186,380,381],{"class":361},"translateY",[186,383,384],{"class":213},"(",[186,386,387],{"class":361},"18",[186,389,390],{"class":203},"px",[186,392,393],{"class":213},");\n",[186,395,397],{"class":188,"line":396},5,[186,398,399],{"class":213},"  }\n",[186,401,402,404,406,409,412],{"class":188,"line":125},[186,403,349],{"class":213},[186,405,353],{"class":352},[186,407,408],{"class":213},"]",[186,410,411],{"class":352},".is-visible",[186,413,414],{"class":213}," {\n",[186,416,417,419,421,424],{"class":188,"line":276},[186,418,362],{"class":361},[186,420,365],{"class":213},[186,422,423],{"class":361},"1",[186,425,371],{"class":213},[186,427,429,431,433,436],{"class":188,"line":428},8,[186,430,376],{"class":361},[186,432,365],{"class":213},[186,434,435],{"class":361},"none",[186,437,371],{"class":213},[186,439,441,444,447,450,453,456,458,460],{"class":188,"line":440},9,[186,442,443],{"class":361},"    transition",[186,445,446],{"class":213},": opacity ",[186,448,449],{"class":361},".85",[186,451,452],{"class":203},"s",[186,454,455],{"class":213},", transform ",[186,457,449],{"class":361},[186,459,452],{"class":203},[186,461,371],{"class":213},[186,463,465],{"class":188,"line":464},10,[186,466,399],{"class":213},[186,468,470],{"class":188,"line":469},11,[186,471,472],{"class":213},"}\n",[11,474,475,476,479],{},"JavaScript 只需要負責一件事：判斷元素進到畫面了沒有。一個 ",[183,477,478],{},"IntersectionObserver"," 就夠了，不需要動畫函式庫。",[11,481,482],{},"真正需要引入動畫套件的情況是時間軸控制、複雜的序列或是與捲動位置綁定的連續動畫。如果只是淡入，那大概是殺雞用牛刀。",[18,484,485],{"id":485},"一個判斷標準",[11,487,488],{},"在加入任何函式庫之前，可以先問：",[94,490,491,494,497],{},[56,492,493],{},"這個功能沒有它做不到嗎？",[56,495,496],{},"它會增加多少載入量？",[56,498,499],{},"三年後還會有人維護它嗎？",[11,501,502],{},"三題裡面有兩題答不出來，通常就不該加。",[18,504,505],{"id":505},"這不是效能潔癖",[11,507,508],{},"網站慢一點，多數訪客不會抱怨，他們只會離開，而你不會知道。",[11,510,511],{},"而且載入量這件事有個特性：它只會往上長，不會自己變少。每個「先加進來再說」的套件，最後都會變成沒有人敢刪的東西。所以在一開始少加一點，會比之後再來瘦身容易很多。",[261,513,514],{},"html pre.shiki code .sCydW, html code.shiki .sCydW{--shiki-default:#D73A49;--shiki-dark:#D73A49}html pre.shiki code .sKWpL, html code.shiki .sKWpL{--shiki-default:#24292E;--shiki-dark:#24292E}html pre.shiki code .se37E, html code.shiki .se37E{--shiki-default:#6F42C1;--shiki-dark:#6F42C1}html pre.shiki code .sMN4m, html code.shiki .sMN4m{--shiki-default:#005CC5;--shiki-dark:#005CC5}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":109,"searchDepth":110,"depth":110,"links":516},[517,518,519,520,521],{"id":296,"depth":113,"text":296},{"id":305,"depth":113,"text":306},{"id":326,"depth":113,"text":326},{"id":485,"depth":113,"text":485},{"id":505,"depth":113,"text":505},"技術選型","2026-07-22","大部分形象網站真正需要互動的地方不超過三個，但它們常常載入了一整套前端框架與動畫函式庫。",{},"\u002Finsights\u002Fhow-much-javascript-does-a-website-need",{"title":282,"description":524},"insights\u002Fhow-much-javascript-does-a-website-need","R08ApjvjFxM1RjX6ESvKnDm_yfO4t9R9OVIfygDAcS0",{"id":531,"title":532,"body":533,"category":628,"date":629,"description":630,"draft":120,"extension":121,"meta":631,"navigation":123,"path":632,"readingTime":125,"seo":633,"stem":634,"__hash__":635},"insights\u002Finsights\u002Ften-steps-into-three.md","把十個步驟變成三個",{"type":8,"value":534,"toc":621},[535,538,541,544,547,558,561,564,570,573,579,585,591,594,597,600,603,606,609,612,615,618],[11,536,537],{},"改善一個流程的時候，直覺的做法是讓每個步驟變快：這個表單少填兩欄、那個畫面少點兩下。",[11,539,540],{},"但通常效果最好的做法不是加速，而是刪除。",[18,542,543],{"id":543},"先把步驟列出來",[11,545,546],{},"不用工具，先用一張紙把目前的步驟一項一項寫下來。寫的時候有幾個規則：",[53,548,549,552,555],{},[56,550,551],{},"用動詞開頭，例如「打開 Excel」「複製昨天的表格」",[56,553,554],{},"包含等待，例如「等主管回覆」",[56,556,557],{},"包含切換，例如「從 LINE 複製到系統」",[11,559,560],{},"寫完之後常常會發現步驟比想像中多。原本以為是三步的事情，攤開來有十一步，其中六步是在不同工具之間搬資料。",[18,562,563],{"id":563},"然後對每一步問一個問題",[11,565,566,567],{},"對每個步驟問：",[24,568,569],{},"如果這一步直接不做，會發生什麼事？",[11,571,572],{},"答案通常會落在三類：",[11,574,575,578],{},[24,576,577],{},"第一類：會出事。"," 這是真正的步驟，留著。",[11,580,581,584],{},[24,582,583],{},"第二類：不會出事，但沒人敢刪。"," 這通常是某次出過錯之後加上去的檢查，而那個錯的原因可能早就不存在了。這一類值得認真討論。",[11,586,587,590],{},[24,588,589],{},"第三類：只是因為工具不支援。"," 「先匯出成 CSV 再匯入另一個系統」這種步驟不是流程的一部分，它是工具的限制。這一類是軟體最容易處理掉的。",[18,592,593],{"id":593},"搬資料的步驟優先處理",[11,595,596],{},"在多數企業流程裡，最花時間的往往不是判斷，而是搬運：把資料從 A 系統複製到 B 系統，把照片從手機傳到電腦再上傳。",[11,598,599],{},"這些步驟不需要人的判斷力，卻佔掉大量時間，而且是出錯機率最高的地方，因為它很無聊。",[11,601,602],{},"處理掉這一類，通常就能把十步變成五步。",[18,604,605],{"id":605},"剩下的步驟才考慮自動化",[11,607,608],{},"把不必要的步驟刪掉之後，剩下的才值得問要不要自動化。",[11,610,611],{},"順序很重要。如果先自動化再簡化，最後常常會做出一個「自動執行不必要步驟」的系統，而且因為它跑得很順，反而更沒有人會去質疑那些步驟。",[18,613,614],{"id":614},"一個提醒",[11,616,617],{},"流程簡化最大的阻力不是技術，是「我們一直都是這樣做的」。",[11,619,620],{},"比較有效的方式不是說服，而是先讓某一個人用新做法跑一次，讓他自己說快很多。這比任何簡報都有用。",{"title":109,"searchDepth":110,"depth":110,"links":622},[623,624,625,626,627],{"id":543,"depth":113,"text":543},{"id":563,"depth":113,"text":563},{"id":593,"depth":113,"text":593},{"id":605,"depth":113,"text":605},{"id":614,"depth":113,"text":614},"產品思考","2026-07-08","流程簡化最有效的方式不是把每個步驟做得更快，而是判斷哪些步驟其實可以直接不存在。",{},"\u002Finsights\u002Ften-steps-into-three",{"title":532,"description":630},"insights\u002Ften-steps-into-three","7uINQhqG9a4VowoN9X8CHS6It_HGQRJAvMQqEQ02clE",1787664334054]