[{"data":1,"prerenderedAt":578},["ShallowReactive",2],{"navigation":3,"project-NoteX":34,"projects-nav":524},[4],{"title":5,"path":6,"stem":7,"children":8,"page":33},"Blog","/blog","blog",[9,13,17,21,25,29],{"title":10,"path":11,"stem":12},"Smart City Governance： PFI Digital Transformation in Taoyuan","/blog/from-mockup-to-market","blog/from-mockup-to-market",{"title":14,"path":15,"stem":16},"How I Built My Design System from Scratch","/blog/how-i-built-my-own-design-system-from-scratch","blog/how-i-built-my-own-design-system-from-scratch",{"title":18,"path":19,"stem":20},"Spatial Data Integration： Disclosing 202 Unregistered High-Risk Segments","/blog/map","blog/map",{"title":22,"path":23,"stem":24},"Performance Diagnostics： Cross-Layer Optimization of Web DRM Reader","/blog/notex-performance-optimization","blog/notex-performance-optimization",{"title":26,"path":27,"stem":28},"The Psychology of Color in UI Design","/blog/psychology-of-color-in-ui-design","blog/psychology-of-color-in-ui-design",{"title":30,"path":31,"stem":32},"The Case for Slow Design in a Fast-Paced Digital World","/blog/slow-design-in-fast-paced-digital-world","blog/slow-design-in-fast-paced-digital-world",false,{"id":35,"title":22,"author":36,"body":40,"date":517,"description":509,"extension":518,"image":519,"meta":520,"minRead":521,"navigation":411,"path":23,"seo":522,"stem":24,"__hash__":523},"blog/blog/notex-performance-optimization.md",{"name":37,"avatar":38},"Emma Thompson",{"src":39,"alt":37},"https://images.unsplash.com/photo-1701615004837-40d8573b6652?q=80&w=1480&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D",{"type":41,"value":42,"toc":508},"minimark",[43,48,52,65,77,89,93,115,119,169,184,188,399,427,447,451,477,481],[44,45,47],"h2",{"id":46},"background","Background",[49,50,51],"p",{},"NoteX is a digital educational workbook platform that helps individual creators legally and safely sell educational materials without a registered legal entity, through a collective licensing agency model. This case study focuses not on feature development, but on \"establishing a verifiable performance diagnostic system within an uncertain and resource-constrained environment.\"",[49,53,54,55,58,59,61,62,64],{},"Market challenges:\n",[56,57],"br",{},"• Individual creators lack legal entity status, making it difficult to sell materials through legal channels.\n",[56,60],{},"• High risk of piracy for educational content\n",[56,63],{},"• Traditional e-commerce platforms cannot protect intellectual property rights",[49,66,67,68,70,71,73,74,76],{},"Project strategy:\n",[56,69],{},"• Centralize licensing and publishing workflows under a legal entity\n",[56,72],{},"• Build a proprietary Web DRM reader as the core delivery interface\n",[56,75],{},"• The reader serves as the copyright protection layer, restricting illegal copying and distribution in the browser",[49,78,79,80,82,83,85,86,88],{},"Technical scope and my role:\n",[56,81],{},"• Infrastructure: WordPress + WooCommerce\n",[56,84],{},"• Responsibilities: Web DRM reader development and integration\n",[56,87],{},"• Development strategy: Rapid prototyping with Lovable AI tools → Technical validation → Integration into WordPress",[44,90,92],{"id":91},"challenges","Challenges",[94,95,96,107],"ol",{},[97,98,99,103,104,106],"li",{},[100,101,102],"strong",{},"Unexpected performance issues after integration"," ",[56,105],{},"\nThe client reported that \"everything feels slow,\" but performance remained poor, with PDF load times (LCP) reaching 6.59s even after implementing Blob-based client-side caching. The problem spanned the frontend, backend, and browser caching mechanisms, requiring cross-layer diagnosis to identify the root cause.",[97,108,109,103,112,114],{},[100,110,111],{},"WordPress caching strategy design",[56,113],{},"\nThe system spans WordPress's EAV structure and custom tables (annotations, reading progress, bookmarks). Different data characteristics require different caching strategies, balancing real-time responsiveness with performance.",[44,116,118],{"id":117},"actions","Actions",[94,120,121,129,146],{},[97,122,123,103,126,128],{},[100,124,125],{},"Identifying the Problem",[56,127],{},"\nNetwork logs revealed redundant downloads of large PDF files—for instance, a 10MB file being fetched twice, taking 5.73s and 6.40s respectively. Since Blob-based caching was implemented correctly, I ruled out frontend issues and determined that the bottleneck stemmed from the HTTP caching strategy rather than the frontend request mechanism.",[97,130,131,103,134,136,137,139,140,142,143,145],{},[100,132,133],{},"Hypothesis Validation",[56,135],{},"\nDesigned an A/B test to validate the hypothesis:\n",[56,138],{},"• A: No Blob, with Header (baseline)\n",[56,141],{},"• B-1: With Blob, no Header (testing Blob benefit in isolation)\n",[56,144],{},"• B-2: With Blob, with Header (final solution)\nObserved LCP, request count, and disk cache behavior.",[97,147,148,103,151,153,154,156,157,161,162,164,165,168],{},[100,149,150],{},"Decision and Implementation",[56,152],{},"\nBackend HTTP layer Header caching:\n",[56,155],{},"• PDF files: ",[158,159,160],"code",{},"Cache-Control: public, max-age=86400"," → long-term caching\n",[56,163],{},"• APIs (progress/bookmarks/notes): ",[158,166,167],{},"Cache-Control: no-cache, must-revalidate"," → real-time sync\nWordPress Transient API: 30-second cache, balancing performance and real-time responsiveness.",[170,171,173,174,173,180],"div",{"style":172},"display: grid; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); gap: 1rem; margin: 2rem 0;","\n  ",[175,176],"img",{"src":177,"alt":178,"style":179},"/project/noteX_A_02.png","NoteX Action 2","width: 100%; border-radius: 0.5rem;",[175,181],{"src":182,"alt":183,"style":179},"/project/noteX_A_03.png","NoteX Action 3",[44,185,187],{"id":186},"execution","Execution",[94,189,190],{},[97,191,192,103,195,197,198,200,202,205,207,208,210,275,277,280,282,283,285,286,288,289,291,292,338,340,343,345,346,348,349,351,352,354,355,357,358,360,361],{},[100,193,194],{},"Case A｜Full Reasoning Chain of Performance Diagnosis",[56,196],{},"\nTrigger: The project's features were complete, but after full integration the client reported \"the reading experience feels slow.\" With the feature layer stable, this task focused on identifying the actual source of the performance bottleneck rather than revisiting feature design.",[56,199],{},[56,201],{},[100,203,204],{},"Phase 1: Initial Observation — Identifying Anomalous Behavior",[56,206],{},"Reviewing network logs in B-1 mode (Blob enabled, no Cache Header), a large 10MB PDF was being downloaded twice. Since the Blob was correctly initialized, frontend issues were ruled out.",[56,209],{},[211,212,213,232],"table",{},[214,215,216],"thead",{},[217,218,219,223,226,229],"tr",{},[220,221,222],"th",{},"Request",[220,224,225],{},"Status",[220,227,228],{},"Size",[220,230,231],{},"Time",[233,234,235,250,262],"tbody",{},[217,236,237,241,244,247],{},[238,239,240],"td",{},"1st admin-ajax.php",[238,242,243],{},"200",[238,245,246],{},"10,599 kB",[238,248,249],{},"5.73s",[217,251,252,255,257,259],{},[238,253,254],{},"2nd admin-ajax.php",[238,256,243],{},[238,258,246],{},[238,260,261],{},"6.40s",[217,263,264,267,270,272],{},[238,265,266],{},"LCP",[238,268,269],{},"—",[238,271,269],{},[238,273,274],{},"6.59s",[56,276],{},[100,278,279],{},"Phase 2: Hypothesis Formation and Controlled Testing",[56,281],{},"• Subjects: B-1 (no Header) vs B-2 (with Header) modes\n",[56,284],{},"• Hypothesis: The bottleneck is not Blob itself, but insufficient information at the HTTP caching layer\n",[56,287],{},"• Finding: B-2 → first download + disk cache 103ms, LCP dropped to 0.60s\n",[56,290],{},"• Inference: Missing Cache-Control / ETag prevented the browser from determining whether content was cacheable",[211,293,294,309],{},[214,295,296],{},[217,297,298,301,304,307],{},[220,299,300],{},"Test Mode",[220,302,303],{},"First Request",[220,305,306],{},"Second Request",[220,308,266],{},[233,310,311,324],{},[217,312,313,316,319,322],{},[238,314,315],{},"B-1 (no Header)",[238,317,318],{},"5.73s download",[238,320,321],{},"6.40s download ❌",[238,323,274],{},[217,325,326,329,332,335],{},[238,327,328],{},"B-2 (with Header)",[238,330,331],{},"4.60s download",[238,333,334],{},"103ms disk cache ✅",[238,336,337],{},"0.60s",[56,339],{},[100,341,342],{},"Phase 3: Controlled Variable Validation",[56,344],{},"• Subjects: Mode A (no Blob, with Header) vs B-2 (Blob + Header)\n",[56,347],{},"• Hypothesis: With correct HTTP Cache Headers, can Blob reduce duplicate requests?\n",[56,350],{},"• Finding: B-2 (Blob + Header) strategy\n",[56,353],{},"  - A (no Blob, with Header): 4 initial requests\n",[56,356],{},"  - B-2 (Blob + Header): 2 initial requests\n",[56,359],{},"• Conclusion: B-2 combination strategy yields the best results",[211,362,363,375],{},[214,364,365],{},[217,366,367,369,372],{},[220,368,300],{},[220,370,371],{},"Initial Load Requests",[220,373,374],{},"Disk Cache Behavior",[233,376,377,388],{},[217,378,379,382,385],{},[238,380,381],{},"A (no Blob, with Header)",[238,383,384],{},"4 (1 download + 3 disk cache)",[238,386,387],{},"3.74s, 3.66s, 28ms, 19ms, 2ms, 1ms",[217,389,390,393,396],{},[238,391,392],{},"B-2 (Blob + Header)",[238,394,395],{},"2 (1 download + 1 disk cache)",[238,397,398],{},"103ms",[170,400,402,415,421],{"style":401},"display:grid;grid-template-columns:repeat(3,1fr);gap:1rem;margin:2rem 0",[403,404,408],"a",{"href":405,"target":406,"style":407},"/project/view-video.html?src=/project/A.mp4","_blank","display:block",[409,410],"video",{"autoPlay":411,"loop":411,"muted":411,"playsInline":411,"preload":412,"style":413,"src":414},true,"metadata","width:100%;aspect-ratio:16/10;border-radius:0.5rem;background:#000;cursor:pointer","/project/A.mp4",[403,416,418],{"href":417,"target":406,"style":407},"/project/view-video.html?src=/project/B-1.mp4",[409,419],{"autoPlay":411,"loop":411,"muted":411,"playsInline":411,"preload":412,"style":413,"src":420},"/project/B-1.mp4",[403,422,424],{"href":423,"target":406,"style":407},"/project/view-video.html?src=/project/B-2.mp4",[409,425],{"autoPlay":411,"loop":411,"muted":411,"playsInline":411,"preload":412,"style":413,"src":426},"/project/B-2.mp4",[94,428,430],{"start":429},2,[97,431,432,103,435,437,438,440,441,443,444,446],{},[100,433,434],{},"Case B｜WordPress Caching Strategy Decision",[56,436],{},"\nDifferentiated caching strategy by data characteristics:\n",[56,439],{},"• HTTP layer: Long-term caching for PDF files (24h); no caching for data APIs (cross-device sync)\n",[56,442],{},"• Application layer: Transient API short-term cache (30s), balancing performance and real-time responsiveness\n",[56,445],{},"Result: API execution time \u003C 1ms, cache hit rate 100% (within 30s)",[44,448,450],{"id":449},"results","Results",[94,452,453,461,469],{},[97,454,455,458,460],{},[100,456,457],{},"Performance improvement",[56,459],{},"• LCP from 6.59s → 0.60s (+91%), initial request count 4 → 2",[97,462,463,466,468],{},[100,464,465],{},"Technical methodology",[56,467],{},"• Established a repeatable performance diagnosis process (Observe → Hypothesize → Validate → Implement)",[97,470,471,474,476],{},[100,472,473],{},"Professional capabilities",[56,475],{},"• Cross-layer analysis, verifiable technical decisions, decomposing problems under uncertainty",[44,478,480],{"id":479},"reflections","Reflections",[482,483,484,492,500],"ul",{},[97,485,486,103,489,491],{},[100,487,488],{},"Redefining Engineering Responsibility",[56,490],{},"\nThis project redefined for me what engineering responsibility means: it's not just completing features, but being able to explain the causal relationship between performance behavior and user experience. The project taught me to proactively clarify responsibilities across the frontend, backend, and environment layers, and to set clear risk boundaries when resources are limited.",[97,493,494,103,497,499],{},[100,495,496],{},"Building a transferable engineering decision framework",[56,498],{},"\nIn NoteX, I treated the performance problem as a system behavior that could be reasoned about and verified, not a singular technical error. The process I built: observe anomaly → form hypothesis → design controlled experiment → validate difference → set stopping criteria. This method doesn't depend on a specific framework or technology—it's used to quickly identify the problem layer, avoid over-optimization, and make engineering decisions that can be traced and explained, in contexts that are uncertain, poorly documented, or have ambiguous responsibility boundaries.",[97,501,502,103,505,507],{},[100,503,504],{},"Internalizing professional transformation",[56,506],{},"\nThe Taoyuan Smart Streetlight project trained me to handle uncertainty at the governance level; NoteX let me practice the same capability at the technical level: building a verifiable methodology in an undefined context. This has become my core professional capability across PM and Engineering, and allows me to make sound decisions in uncertain scenarios.",{"title":509,"searchDepth":429,"depth":429,"links":510},"",[511,512,513,514,515,516],{"id":46,"depth":429,"text":47},{"id":91,"depth":429,"text":92},{"id":117,"depth":429,"text":118},{"id":186,"depth":429,"text":187},{"id":449,"depth":429,"text":450},{"id":479,"depth":429,"text":480},"2025-04-24T00:00:00.000Z","md","/project/noteX_A_01.png",{},8,{"title":22,"description":509},"bcBx_fk_6en8U9_o-zUfbdWPx8EykaA30AvXe91hqR8",[525,544,561],{"id":526,"title":527,"date":528,"description":529,"description_en":530,"extension":531,"image":532,"meta":533,"stem":541,"tags":542,"title_en":10,"url":536,"__hash__":543},"projects/projects/ecotrack.yml","全台首個智慧城市 PFI 實踐：桃園智慧路燈轉型","2022-01-01T00:00:00.000Z","在 15 年期的 PFI 轉型架構下，透過需求治理與角色分層設計,整合四套舊系統為單一平台,服務六類使用者、管理 162,000 盞智慧路燈。","Unified four legacy systems into a single platform through governance design and role-based access control, serving six user types and managing 162,000 smart streetlights across Taoyuan.","yml","/project/Taoyuan_A_01.png",{"path":534,"body":535},"/projects/ecotrack",{"title":527,"title_en":10,"description":529,"description_en":530,"image":532,"url":536,"tags":537,"date":540},"/projects/smart-streetlight",[538,539],"PM","UIUX","2022","projects/ecotrack",[538,539],"V009I0epBDzP5dY0auyCYfIpTRof9I76xp-8HQagELI",{"id":545,"title":546,"date":547,"description":548,"description_en":549,"extension":531,"image":519,"meta":550,"stem":558,"tags":559,"title_en":22,"url":553,"__hash__":560},"projects/projects/notex.yml","效能診斷：Web DRM 閱讀器的跨層診斷與優化","2025-01-01T00:00:00.000Z","藉由跨層診斷與 A/B 測試精確定位效能瓶頸，成功優化 PDF 載入表現，將反應時間由 6.59s 壓低至 0.6s，效能增長達 91%。","Identified performance bottlenecks through cross-layer diagnostics and A/B testing, successfully optimizing PDF load times from 6.59s to 0.6s—a 91% increase in efficiency.",{"path":551,"body":552},"/projects/notex",{"title":546,"title_en":22,"description":548,"description_en":549,"image":519,"url":553,"tags":554,"date":557},"/projects/NoteX",[555,556],"Frontend","WordPress","2025","projects/notex",[555,556],"RlYBo4rkSR8bZkg3QdBy3oXQfNgpwAesUKhy4NatE9w",{"id":562,"title":563,"date":564,"description":565,"description_en":566,"extension":531,"image":567,"meta":568,"stem":575,"tags":576,"title_en":18,"url":569,"__hash__":577},"projects/projects/map.yml","空間資料整合：揭露台北市 202 條未登錄防災風險路段","2026-01-01T00:00:00.000Z","建構台北市首個窄巷空間驗證系統，整合 5 大開放資料集以自動化識別 202 條未登錄風險路段，並實作全市 1,672 條路段、總長 9.18 公里的完整圖資整合。","Engineered Taipei’s first spatial validation system to automate the identification of 202 unregistered risk segments, achieving a full integration of 1,672 segments totaling 9.18 km.","/project/map_A_01.jpg",{"path":569,"body":570},"/projects/map",{"title":563,"title_en":18,"description":565,"description_en":566,"image":567,"url":569,"tags":571,"date":574},[572,573],"Fullstack","GIS","2026","projects/map",[572,573],"gAEhNEi9ow33wIkLgYEhq471cxo5eKUSTQFUKw15NDM",1775314583932]