除錯的意義


上週五工作到一半,家裡的門鈴響起,開門後是衛生局的人員通報說社區附近出現登革熱案例,需要整棟樓進行消毒,正當我想著那我就不要出門就好,結果想不到對方說連屋內都要消毒時,我一整個傻眼,家裡這麼多吃的穿的睡的,全部都要暴露在殺蟲劑下,一想到頭就暈。

第一時間先上網查一下這是不是新型態的詐騙手法,才知道原來對臺南人來說這已是司空見慣的作業流程,還查到「養生膠帶」這個神器,它可以把大型傢俱鋪上一層塑膠袋作為防護,於是當晚趕快去水電行買了兩捲,想說隔天早上再蓋就好。

到了隔天一早九點,大樓就傳來噴藥機的巨大聲響,衛生局人員敲每戶的門,說要開始進行消毒了,那時候我們才剛吃完早餐,一整個被殺個措手不及,只好趕快把養生膠帶鋪上,但因為太趕,很多傢俱都沒有鋪到,就這樣半推半就的被請出家裡。

坐進電梯前看到消毒人員拿著在戶外消毒水溝的大型噴藥機直接在家裡面狂噴,不知為何有種荒謬感,好像我家是喪屍病毒外洩的實驗室必須徹底消毒一番,然後跟著鄰居們一起被迫撤離家園,似乎也拉近了鄰居之間的距離(?)

就這樣在外面待了半小時後回到家中準備進行清掃,一進門就被殺蟲劑的味道嗆到一直咳,只能把所有門窗打開用電風扇狂吹,然後依序拆下養生膠帶並把每個傢俱都用酒精擦擦過一遍,地板因為還有殘留所以很濕滑,必須要先用靜電拖拖過一輪。

第一輪收拾殘局就花了一個多小時,接下來開始每週的例行打掃,掃完後開始洗衣服跟床單,前前後後花了三個小時才恢復世界和平,打掃過程中就只有撿到一隻小小強,好像在跟我證明說消毒是有用的。

在實體世界消完了毒,數位世界的蟲依舊每天出現,真希望也有台噴藥機可以一次幫我搞定 Debug 啊~

除錯的意義

在忙完了 WordCamp 之後,我調整了自己的工作節奏,從原先做二休一,改成做五休二,把之前累積的工作放在早上集中力最高的三小時來處理,然後上午做完後就下班了,下午看是要繼續弄產品,還是去跑個步動一下。

這樣的節奏在第一週運作的很順利,看到專案每天都有進展對客戶也有所交代,但到了第二週開始有些狀況,下午的時間常收到客戶的緊急回報,因為這些問題會影響到客戶當晚的作業流程,因此我會放棄下午的行程優排除客戶的問題。

檢查後才發現是第一週開發的功能所影響的,這些新功能開發完成時雖然我都有進行測試,並且確認可以正常運作後才交付,但這週遇到的問題是我完全沒想到的情境,我沒有預料到做了 A 功能竟然會造成 B 功能的錯誤,於是只能當客戶在實際使用時才發現問題。

這樣的狀況持續了幾天,結果變成一整天都在處理專案,上午開發下午除錯,就讓我反省為什麼會有這些臭蟲的發生?難道這些蟲是無法避免的嗎?就算無法避免有沒有更好的通知機制能讓我第一時間就知道?

因此我想藉由這篇文章來分析一下這週遇到的問題,並且提出可能的解決之道。情境是這樣:

因為 wp_postmeta 資料表已經儲存百萬筆的資料,在載入文章編輯頁的時候速度變得很慢,原先的欄位是用 ACF Repeater 開的,這些資料每天都會有數百筆的增長,因此我跟客戶提議把這些資料拉出來,新開一個自訂資料表來存放以及查詢,並且用 WP_List_Table 來彙整所有資料,不用點進各別文章查看。

我將本週遇到的問題類型分為以下三種:

ㄧ、原本應該發生的行為沒有觸發

重構前有一個功能是可以在前台寫入這個資料,然後在寫入後會觸發第三方的 API 做事情,結果重構後觸發 API 的行為沒有發生。觀察我自己的重構方式是在加入新的寫入方式後,把原先舊有的程式碼全部註解掉,結果我以為註解掉的部分就只有寫入資料的功能,想不到觸發 API 的程式碼也被我一併刪了。

舊版寫入資料的方法有將近四十行程式碼,觸發第三方 API 僅有六行,因此一整遍看過去會以為這四十六行就是只有處理寫入資料,而忽略了觸發 API 也在裡面。想到幾個可以改善的地方是:

  1. 把寫入資料的程式碼包裝成方法或函式,這樣在視覺上就會是一個獨立的事件而非一連串的行為
  2. 增加明顯的程式碼註解,帶有開頭跟結尾,並且把空行的間距加大,讓自己可以很明顯的看出處理同一功能的區塊開頭與結尾
  3. 增加返回的型別提示,確實定義每個任務執行完後要回傳的是什麼資料型態,以這個例子來說,寫完資料觸發 API,要回傳的應該是 WP_HTTP_Response 物件,如果我有指定的話,在測試重構後的方法我就可以即時知道回傳東西有誤,進而發現到我漏了觸發 API 的行為

事實上我有時候都會懶得把一連串的程式封裝成方法或函式,因為每多一個函式就要多寫程式碼,像是函式的命名、帶入參數的定義、返回資料類型、註解說明,尤其是要找問題的時候必須在多個函式之間切換很麻煩,但為了程式碼的維護性,還是要逼自己養成這個習慣才行。

還好在我改用 PhpStorm 之後裡面有一個很貼心的重構功能,可以把選取的程式碼直接變成函式:

點下之後就會在同一個檔案的最下方加入新的方法,他還會自動所需要的參數帶入,並且寫好參數的註解,算是省下不少手動輸入的時間,另外在查看函式的部分,只要把游標停留在函式上,按住 cmd + 滑鼠點擊,就會自動跳到定義該函式的區塊或檔案以方便查看。

有了這兩個實用的功能應該不能再用麻煩當藉口了,乖乖的把程式碼封裝起來吧!

二、重構不完整

新功能有一個方法是檢查特定的 user id 是否有在新建立的資料表中,藉此取代原本判斷 user id 是否存在於 Post Repeater 欄位中的邏輯,這部分我已經有獨立一個方法來做判斷,因此我是直接替換這個方法裡面的判斷邏輯。

結果想不到除了這個方法以外,還有一支散落於某處的函式也是在處理一樣的功能,結果我只改了方法沒改到函式因此發生問題,這讓我學到在重構方法前,記得先用全站搜尋功能來尋找原始的 user id 判斷邏輯有寫在哪寫地方,然後列出所有的方法與函式。

更好的做法是要將這些重複的地方整合為一個方法,就能避免改了 A 忘了 B,趁著這次除錯就可以順手把這件事做起來,如果時間趕可以先寫個註記,萬一之後又要調整就不會漏了它。

三、預期外的 save_post 勾點

新增資料的部分我是用 ACF 設定頁來處理,這樣就可以省下開一堆欄位的時間,然後當儲存後抓欄位值寫入資料,一般儲存文章的勾點是 save_post,而 ACF 的是 acf/save_post,問題出在我為了要判斷當下儲存的頁面是 ACF 設定頁,因此用了 get_current_screen() 來取得目前所在頁面,當頁面 ID 等於我的設定頁 ID 時才會執行資料寫入。

因為這個勾點的名稱我預期是只有在儲存 ACF 設定頁面才會觸發,為了精簡程式碼我把 get_current_srceen() 的判斷拔掉了,想說應該不需要這層判斷,但沒想到一般文章在儲存時也會觸發到,這導致寫入資料表時少了必要欄位而噴錯,造成客戶沒有辦法在後台更新文章。

於是還是乖乖的把判斷加回去,但第二個問題來了,因為我有用另外一個 ACF 設定頁以表單的方式放在前台供客戶使用,在前台因為沒有 get_current_srceen() 這個方法,因此設定頁 ID 的判斷會出錯,要先用 function_exists() 來判斷函式是否存在,才能繼續做設定頁 ID 的判斷。

第一個問題我想到的解決辦法就是用任何勾點前先去爬一下原始碼,看他們會在哪裡被觸發,使用 PhpStorm 並且有設定 WordPress 為專案環境的話,在每個勾點前面都可以看到一個小 h 的圖示,點擊後就會直接前往勾點定義的來源:

第二個問題的解法是在使用 WordPress 內建的函式時,要先意識到該函式是否有前後台的區別,如果懶得想最快的解法就是用之前一律做 function_exists 判斷就不會錯了,然後當回傳 false 時直接返回終止程式執行。

-

回想我的除錯過程都是客戶貼重大錯誤的截圖給我,然後我再進主機或是網站後台去查看錯誤日誌,然後從裡面尋找程式出錯的地方,我就在想不知道有沒有更快速的方法可以得知問題在哪,爬了一下找到這個解:https://www.businessbloomer.com/woocommerce-email-fatal-error/​

研究後才知道原來 PHP 有 register_shutdown_function 函式,而 WooCommerce 就是利用這個函式來來做錯誤日誌的紀錄,並且在裡面放 woocommerce_shutdown_error 的勾點,就能實現當 500 Error 時收到通知,而不用從伺服器底層處理。

我期許自己在除錯的時候不是只要把眼前的問題解決就好,而是有餘裕去反思為何這個問題會發生,並且在除完錯的當下重構程式碼來避免再次出現,甚至可以改進工作流程來減少緊急狀況的發生,為了努力實現只上半天班,一定要將重大錯誤發生的頻率降低才行啊~

WordPress 開發日常

Read more from WordPress 開發日常

之前朋友約出來喝咖啡時他分享下一個階段的 AI 可能會發展成監督式的 AI,也就是當 AI 自己在跑的時候,會有另外一個 AI 來檢查它現在做的每一件事情是否都真的有往正確的方向推進。 當下聽到覺得這個 idea 很棒,但具體要怎麼實作完全沒有概念,更不用說這樣子 token 消耗的成本會不會變成兩倍?而且監督 AI 的 AI 需要有什麼能力?該以哪一種方式來進行監督?然後又該如何把找出的問題回報給原本的 AI 呢? 這一切問題在我最近研究 Claude Code 以外的工具時意外發現了解決方案,那就是之前我拿來做為 Discord Bot 的 AI 引擎 OMP。 OMP 是 PI 的分支,想理解什麼是 OMP 可以先從 PI 開始理解,PI 是一套開源且輕量化的 Coding CLI,也是跟 Claude Code 一樣跑在終端機的寫程式 Agent。 相較於 Claude Code 的封閉,PI 完全開源,並且支援多家不同的模型,PI 強調有需要什麼工具再自己額外安裝,就不會像 Claude Code 一樣原廠給你什麼就只能用什麼。 而 OMP 是 PI 的分支,它的概念跟...

之前分享我請 AI 設計的一套專門用來管理手邊待辦清單的專案管理軟體,邊用的時候我就在邊思考著:當我新增這些任務之後,我還是要回到終端機去把這些任務的描述、相關的文件以及完成的目標告訴 AI,然後等它跑完之後我再驗收,再回到我的計時軟體裡面來記錄是已完成還是該暫停。 如果可以在開立任務的時候,就能直接指派 AI 來做這件事情,是不是就可以少一個重複描述、開發需求的流程?剛好有了這個想法之後,就在 YouTube 上面看到一個叫做 Auto Claude 的專案。 它把 Claude 直接跟看板軟體整合,並且可以在裡面詢問 AI 關於專案的細節以及產生開發藍圖,也能設定專案的亮點與相關資訊。 我看到後驚為天人,覺得這似乎是另外一種理想的 AI 開發軟體,本想直接下載裝起來用,但請 Claude 研究後發現它有一些問題,於是就想說自己來開發,順便理解關於整個 Loop Engineering 的概念。 Claude Orchestrator 這次一樣是使用 Glaze 來開發,請它建立好專案的環境之後,然後再回到終端機去接手後續的多工並行。我的做法是先請 AI 去參考 Auto...

常常在社群媒體上看到各種自動化工具的廣告,從早期的 n8n 開始到現在 AI Agent 自動完成任務,這些工具跟流程都很棒。但是回到使用者本身,我們真的知道自己有哪些工作是可以被自動化或是應該「自動化」的嗎? 這就讓我回想起以前給自己的工作要求,那就是要把手上所做的每一件事情都記錄下來,並且可以明確看到每項任務的執行時間以及頻率,這樣事後才有辦法做回顧,我把這樣的理念也應用到承接專案的層面上。 這也是為什麼我有一些專案都是採取時薪計價的開發模式,因為我需要讓客戶知道他的預算都花在哪些項目,來藉此把資源用在合適的任務上。 我最早使用的即時軟體叫做 Ora,這是一套結合看板管理、敏捷開發流程、時數統計,甚至還有內建聊天討論串的一套強大工具。 它長期都是免費的,付費方案是團隊成員超過計算,所以我都是使用它來進行與客戶的專案管理,它的介面十分清楚且一目瞭然,可以縱觀地知道是哪一類型的任務花最多時間,也可以細看到具體是在處理哪一個問題所消耗的時數。 但當某天我打開準備使用它的時候,發現它被另外一套專案管理軟體 ClickUp...