管理工具的設(shè)計與實踐)
我給手頭的這個工具起名叫caveman中文直譯過來就是“穴居人”。名字聽著像開玩笑但它其實是我用過之后覺得最貼切的一個代號——這個工具做的事非常原始任務(wù)寫在文本文件里操作靠命令行沒有數(shù)據(jù)庫沒有網(wǎng)絡(luò)依賴沒有花哨界面。它解決的是我被各種“全家桶式”效率軟件折磨到崩潰之后最樸素的那個需求隨手記下來、隨時查得到、數(shù)據(jù)永遠歸我。這篇文章就把這個叫 caveman 的小項目掰開揉碎講清楚包括它名字的由來、核心設(shè)計思路、真實的開發(fā)過程、進入日常使用后的組合玩法以及極簡工具在什么場景下該用、什么時候千萬別用。如果你也受夠了打開一個 App 要等三秒、數(shù)據(jù)存在別人服務(wù)器上、功能多到根本用不完的現(xiàn)狀這篇內(nèi)容應(yīng)該能給你一點不一樣的參考。1. 從“洞穴人”這個名字說起一個工具為什么要故意做舊1.1 軟件復(fù)雜到讓人喘不過氣我做 caveman 之前試過不少任務(wù)管理工具。Notion 功能全但每次打開都要經(jīng)歷漫長的加載Jira 適合團隊但為了看自己的待辦還得穿過好幾個面板手機上的效率 App 更別提了一個比一個精致一個比一個費電而且?guī)缀跛性诰€工具都在“收集數(shù)據(jù)”這件事上有自己的想法。時間長了你會發(fā)現(xiàn)一個矛盾我明明只是想在指尖閃過一個念頭的時候用五秒鐘把它留住結(jié)果卻要先登錄、再建文檔、再選模板、再調(diào)樣式。等這一套走完那個念頭早就沒了。后來我想明白一件事對個人日常記錄來說工具的復(fù)雜度和記錄頻率是成反比的。越是“強大”的工具啟動成本越高你越是懶得用它。真正撐起日常記錄的往往是那些不起眼的、隨時能抓起來寫的載體。caveman 就是在這種反思里冒出來的。1.2 三個設(shè)計約束決定了它的全部性格caveman 的定位不是一個功能齊全的軟件而是一個故意“做舊”的個人任務(wù)臺賬。為了讓這種“原始”不只是一句口號我從第一天就給它定下了三個硬性約束單文件可運行整個工具就是一個caveman.py日常使用只需python3 caveman.py list不需要安裝、不需要服務(wù)、不需要前端構(gòu)建。零第三方依賴只用 Python 標準庫連requests都不用。這意味著不管機器是新的還是舊的Python 裝了就能跑斷網(wǎng)也能跑。純文本即數(shù)據(jù)所有數(shù)據(jù)落在一個叫tasks.txt的文件里沒有 SQLite 的二進制文件沒有 JSON 的多層嵌套。這三個約束不是拍腦袋定的。單文件是為了降低使用門檻——你不需要記住“配置文件在哪”“數(shù)據(jù)庫在哪”“日志在哪”一切都在一起零依賴是為了讓生命周期足夠長——Python 2 到 Python 3 的遷移很難但一個只用標準庫的腳本十年后拉出來改兩行還能跑純文本則是為了數(shù)據(jù)主權(quán)——無論工具以后還活不活著.txt文件永遠能用記事本打開。1.3 數(shù)據(jù)主權(quán)比“順手”更重要的價值市面上大多數(shù)任務(wù)工具都在做“托管服務(wù)”你負責(zé)輸入它負責(zé)替你保管和渲染。聽起來很方便但你有沒有想過如果這個產(chǎn)品停止運營了你的數(shù)據(jù)怎么辦如果是訂閱制你停止付費之后那些寫了三年的日志還能不能導(dǎo)出成可讀格式caveman 對這個問題給出了一個非常直接的答案你的數(shù)據(jù)本來就是一個文本文件有一份就在你自己電腦上。你可以拿它做 Git 版本管理可以用網(wǎng)盤同步可以隨手拷進 U 盤可以放到任何一臺有 Python 的機器上直接查看。它不產(chǎn)生“平臺鎖定”因為文本格式本身就是最底層的兼容協(xié)議。我身邊很多開發(fā)者朋友第一次看這個工具都會問一句“就這”。但我用了幾個月后最大的體會是工具的價值從來不在于它提供了多少個按鈕而在于它在你最需要記錄的時候能不能零阻力地出現(xiàn)。caveman 這個名字就是在提醒自己不要忘記這一點。2. caveman 的核心設(shè)計文本即數(shù)據(jù)庫命令即思維2.1 為什么不用 SQLite也不用 JSON有人會說既然要寫任務(wù)管理工具那至少用個 SQLite 吧或者把數(shù)據(jù)存成 JSON結(jié)構(gòu)一目了然解析也方便。我最初也猶豫過但把幾種方案放在一起對比之后發(fā)現(xiàn)純文本反而是最適合這個場景的。存儲方案人眼可讀性手工修改友好度git diff 友好度依賴要求純文本極好極好極好無JSON一般容易改錯括號勉強可用標準庫YAML較好縮進容易出錯一般第三方SQLite差必須借助工具無法直接 diff第三方核心邏輯是這樣的如果數(shù)據(jù)格式設(shè)計成“人眼也能直接讀”那么即使某天 caveman 這個程序不見了你依然可以用任何文本編輯器打開tasks.txt一眼看明白有哪些任務(wù)、哪些做完了、優(yōu)先級是什么。數(shù)據(jù)不應(yīng)該被程序綁架程序只是數(shù)據(jù)的過客。這一點純文本是做得最好的。JSON 也有它的優(yōu)勢比如解析標準、結(jié)構(gòu)清晰但對于“手工維護”這個場景JSON 的括號和轉(zhuǎn)義規(guī)則太反人類了。而且 JSON 文件在 git 里做 diff 的時候一行的改動往往導(dǎo)致整個結(jié)構(gòu)看起來都變了實際幾乎沒法逐行審查。純文本則完全不同每一行是一條獨立記錄哪個任務(wù)改了、哪條完成了git diff 出來非常清楚。2.2 tasks.txt 的任務(wù)結(jié)構(gòu)和讀寫規(guī)則caveman 的數(shù)據(jù)文件結(jié)構(gòu)長這樣# caveman task file # format: [status] [priority] [date] [description] x [1] 2025-01-10 完成 API 接口聯(lián)調(diào) [2] 2025-01-12 修復(fù)登錄頁按鈕錯位 x [1] 2025-01-12 整理會議紀要并發(fā)送給相關(guān)同事 [3] 2025-01-13 給服務(wù)器做一次安全巡檢每一行一條任務(wù)規(guī)則非常簡單行首是x表示已完成空一個字符表示待辦第三位是[優(yōu)先級]1 最高3 最低接下來是YYYY-MM-DD日期日期后面剩下所有內(nèi)容都是任務(wù)描述允許帶空格、標點、方括號等任意字符。這里有個刻意設(shè)計已完成的任務(wù)行不會被刪除只會把行首從空格改成x。這樣一來tasks.txt就自動變成了一本流水賬——你不僅能看到“現(xiàn)在還有哪些沒做”還能回顧“過去某一天完成了什么”。這個特性在周報、月度總結(jié)的時候特別好用。關(guān)于任務(wù)編號我做了另一個故意簡單的決定行號就是任務(wù) ID。文件里第幾行就是幾號。雖然任務(wù)增刪時行號會變但反正所有操作都是操作一個任務(wù)文件先list看到當(dāng)前行號再對那個行號執(zhí)行操作就行。這個方案省掉了自增 ID 的所有維護成本也避免了“刪掉中間一條后編號斷檔”的問題。2.3 命令設(shè)計先想人類怎么說再想代碼怎么寫我見過不少工具功能不錯但命令命名非常勸退比如task --create --title xxx --priority high。這種設(shè)計對程序是友好的對人不友好。caveman 的命令設(shè)計原則是先想人在真實對話里會怎么表達再映射到命令上。命令實際作用人在說話時的自然表達caveman list列出所有未完成任務(wù)“現(xiàn)在有啥事在排隊”caveman add 任務(wù)描述 -p 1添加一條任務(wù)“記一下這個事優(yōu)先級高”caveman done 3完成指定編號的任務(wù)“第3件事搞定了”caveman note 記錄內(nèi)容寫一條工作日志“順便記一筆”caveman review復(fù)盤今天的完成情況“今天這一天過得怎么樣”所有命令都盡量控制在兩三個單詞內(nèi)參數(shù)能省就省。比如添加任務(wù)時優(yōu)先級默認是 2普通只有需要標紅的時候才手動寫-p 1done允許多個編號一次性傳入比如caveman done 3 5 7對應(yīng)人類的“連劃三件事”這個動作。2.4 核心代碼邏輯解析與寫回解析邏輯我一開始用正則寫后來踩了幾個坑下一章詳細講改成位置式解析。核心思路很樸素順著每一行從頭往后讀狀態(tài)位、優(yōu)先級、日期都是固定格式讀完之后剩下的整段都當(dāng)作描述不再做任何規(guī)則匹配。def parse_tasks(lines): tasks [] for idx, line in enumerate(lines, 1): if not line.strip() or line.startswith(#): continue done line.startswith(x) rest line[1:].lstrip() try: prio int(rest[1:2]) # 形如 [1] date rest[3:13] # 2025-01-12 desc rest[14:].strip() # 剩下的全當(dāng)描述 tasks.append({ id: idx, done: done, priority: prio, date: date, desc: desc, }) except (ValueError, IndexError): # 非標準行直接跳過保證整個文件可讀 continue return tasks寫回的時候有一個很重要的細節(jié)必須做原子寫入。也就是先把修改后的內(nèi)容寫到一個臨時文件里再通過os.replace把臨時文件替換成原文件。這樣即使寫的過程中斷電了原文件也不會變成半個壞文件。def write_tasks(path, lines): tmp path .tmp with open(tmp, w, encodingutf-8, newline) as f: f.writelines(lines) os.replace(tmp, path)文本 ??? ??? ? ????. ??? “??? ?? ? ??, ?? ????? ?? ? ??, ????? ???? ???? ???”? ????.3. 從零到上線caveman 的實際開發(fā)時間線3.1 第一版原型一個下午跑起來caveman 的第一版是在一個周日下午寫出來的。當(dāng)時手上有一個項目排期表亂成一團下午剛開完需求會我坐在電腦前把需求寫在紙上能加任務(wù)能列出任務(wù)能勾掉任務(wù)數(shù)據(jù)要存在本地文本里這四個需求聽起來少得可憐但幾乎覆蓋了日常任務(wù)管理的全部核心動作。我從命令行入口開始寫用的就是 Python 自帶的argparse命令設(shè)計成子命令的形式先寫list再寫add最后補上done。第一版大概三百行跑通之后我沒有立刻加功能而是直接用真實的日常工作開始測試——這也是我覺得最重要的一步先放回真實場景里去用驗證它是否真的解決問題再決定要不要繼續(xù)加代碼。大概一周之后我才補上了note和review。note對應(yīng)的是“隨手記一筆”的需求review對應(yīng)的是“每天下班前看一眼今天完成了什么”。這兩個需求在原型階段就存在但我刻意壓著不加因為想確認少了它們流程是不是真的跑不順。結(jié)果發(fā)現(xiàn)只靠list和done任務(wù)臺賬更像是“待辦清單”缺少“記錄發(fā)生過的內(nèi)容”這個維度。后來把note加上之后整個工具才從任務(wù)管理變成了一個真正的工作日志。3.2 踩坑實錄文本解析的五個邊界問題純文本方案看起來簡單但真正寫起來還是會遇到不少邊界情況。我把過程中踩過的比較有價值的坑列出來給同樣想做文本型工具的朋友提個醒。第一個坑是任務(wù)描述里的方括號會導(dǎo)致正則解析錯亂。最早我用^[ x] \[(\d)\] (\d{4}-\d{2}-\d{2}) (.*)$這一套正則去切行表面正??梢坏┤蝿?wù)描述里出現(xiàn)[2025-01-12]或[bug#123]之類的內(nèi)容匹配就直接失敗了。解決方式很簡單放棄正則改成位置式解析。狀態(tài)位、日期、優(yōu)先級都是固定長度或固定格式的前綴讀完之后剩下的部分不做任何匹配全部視為描述。第二個坑是中文對齊問題。一開始我想讓list輸出得漂漂亮亮用str.ljust做列對齊結(jié)果中文字符的寬度和英文字符不一樣輸出直接錯位。我糾結(jié)了幾分鐘后決定不做對齊。輸出只用一個簡單的[1]前綴加優(yōu)先級標記反而更清晰。這個選擇也符合 caveman 的整體氣質(zhì)——不追求美觀追求一眼能看懂。第三個坑是Windows 編碼兼容。我的主要使用環(huán)境是 macOS但部分腳本會在 Windows 上跑。最初用 UTF-8 寫文件Windows 記事本打開就亂碼。后來讀寫統(tǒng)一使用utf-8-sig編碼帶 BOM 的 UTF-8記事本能正常識別Linux/macOS 也能正常讀兩邊都照顧到了。第四個坑是換行符差異。Windows 用\r\nLinux/macOS 用\n。如果直接按文本模式讀寫在 Windows 上可能會遇到多換一行的問題。我的做法是讀文件時用newlineNone讓 Python 自動識別寫文件時統(tǒng)一指定newline保證跨平臺行為一致。第五個坑是done 操作的冪等性。用戶對一條已經(jīng)完成的記錄再次執(zhí)行done程序應(yīng)該怎么辦我最初直接報錯后來發(fā)現(xiàn)這個設(shè)計很煩人——因為在真實操作中你可能會連續(xù)對同一個編號按回車或者腳本批量執(zhí)行時重復(fù)調(diào)用。最后改成如果任務(wù)已經(jīng)是完成狀態(tài)不做任何修改直接打印“已是完成狀態(tài)”。命令要冪等這是工具腳本一個很重要的原則。3.3 平臺適配編碼、換行與終端別名caveman 的跨平臺適配沒有做什么高級處理真正的重點都集中在文件讀寫這一層因為其他邏輯都是純 Python 字符串處理不涉及平臺差異。我把文件讀寫單獨封裝成一個模塊里面統(tǒng)一處理編碼、換行、臨時文件替換這樣不管是 Windows 還是 macOS行為都是一致的。日常使用的時候給命令加個別名能省掉大量鍵盤輸入。macOS 或 Linux 用戶可以在~/.zshrc或~/.bashrc里加alias cmpython3 ~/tools/caveman.py alias cmaddpython3 ~/tools/caveman.py addWindows 用戶可以創(chuàng)建一個cm.bat放在任意目錄并加入 PATHecho off python %USERPROFILE%\tools\caveman.py %*加完別名之后日常操作基本就變成了cm看任務(wù)列表cmadd 給博客加一篇草稿記錄任務(wù)cm done 5劃掉一條。整個過程都是在終端里完成的速度和手感不是圖形化應(yīng)用能比的。3.4 給零依賴項目寫自測腳本有人會擔(dān)心一個零依賴的命令行工具怎么保證改來改去不出問題我用的是 Python 標準庫自帶的unittest測試用例直接操作臨時文件目錄跑完自動清理不需要任何額外安裝。測試覆蓋了幾個關(guān)鍵場景添加任務(wù)后文件里是否追加了正確格式的行完成一個任務(wù)后行首是否從空格變成了x對已完成任務(wù)重復(fù)執(zhí)行done文件內(nèi)容是否不變?nèi)蝿?wù)描述包含中文、方括號、特殊字符時解析是否正??瘴募跏蓟瘯r能否正常運行不報錯。這些用例雖然簡單但給了我折騰后續(xù)版本的安全感。一個工具腳本不需要測試覆蓋每一行但幾個核心場景必須有回歸保護尤其是格式解析這類容易改壞的地方。4. caveman 進入日常真實工作流中的組合用法4.1 每天的任務(wù)臺賬早晨列、傍晚清我用 caveman 的方式已經(jīng)基本固定成了一日流程。早上到工位第一件事是cm屏幕上會按優(yōu)先級列出當(dāng)前所有未完成任務(wù)。這時候我會把今天必須推進的事排在心里然后開啟工作。遇到新任務(wù)、新反饋隨手cmadd一條優(yōu)先級按“重要且緊急”的原則給 1普通順手的事情給 2 或 3。這里有個經(jīng)驗不要事事都給 P1P1 給多了等于沒有 P1。普通任務(wù)默認 P2只有真正影響今天目標的事情才標 P1。傍晚下班前我會跑一次cm review它會統(tǒng)計今天的完成數(shù)和剩余數(shù)同時把今天note的內(nèi)容一起列出來。這套流程堅持下來之后我對“今天有沒有做正事”這個問題有了非常直觀的答案——不用回憶不用翻聊天記錄打開終端跑一條命令一天的工作軌跡都在那里。4.2 讓 caveman 融入自動化別名與定時任務(wù)caveman 是純命令行工具所以天然適合和其他桌面自動化流程嵌在一起。我常用的一個做法是配合系統(tǒng)的快捷鍵綁定在 macOS 的 Automator 或第三方快捷指令里調(diào)用cmadd綁定一個全局快捷鍵比如Ctrl Option A。這樣不管我在寫代碼、看文檔還是開會只要腦子里閃過一個需要跟進的事項按一下快捷鍵輸入框彈出來敲完回車任務(wù)就已經(jīng)落到tasks.txt里了。整個過程三秒以內(nèi)沒有解鎖手機、沒有打開 App、沒有等待加載。另一個用法是配合系統(tǒng)的定時任務(wù)。比如我每天下午 5 點半會跑一個任務(wù)自動執(zhí)行caveman review并把輸出重定向到當(dāng)天的工作日志文件里。這樣即使我某天忘記做復(fù)盤數(shù)據(jù)也會被自動保存下來事后補看完全沒問題。4.3 多端同步與文件備份純文本的天然優(yōu)勢純文本的另一個巨大優(yōu)勢是同步和備份極其簡單。我自己的方案有兩層第一層是Git 版本管理。tasks.txt所在的目錄初始化成一個 Git 倉庫每天或每周 commit 一次。因為每一行都是一條獨立記錄git diff 看得清清楚楚哪天加了哪條任務(wù)、哪天完成了哪條、哪天寫了什么筆記。這種“任務(wù)變更歷史”在沒有任何額外代碼的情況下就自動獲得了。第二層是SyncThing 或網(wǎng)盤同步。我用 SyncThing 把整個目錄同步到手機和工作電腦上。在手機上也可以用任意文本編輯器直接查看tasks.txt雖然體驗不如原生 App 舒服但應(yīng)急查一條任務(wù)完全夠用。純文本同步幾乎不會出現(xiàn)“文件損壞”的問題最多只是兩個端都有修改時產(chǎn)生版本沖突而文本沖突的合并成本很低。4.4 從 txt 到周報幾條命令的導(dǎo)出方案caveman 沒有內(nèi)置生成周報的功能但純文本配合 shell 管道導(dǎo)出方案可以非常靈活。比如我要統(tǒng)計這周完成了多少條任務(wù)只需要grep ^x tasks.txt | grep 2025-01- | wc -l要列出本周完成的內(nèi)容變成 Markdown 列表grep ^x tasks.txt | grep 2025-01- | sed s/^/ - [完成] /再配合note的內(nèi)容一個工作周報就基本成型了。你還可以把這個導(dǎo)出命令寫進一個report.sh每周五下午跑一次輸出直接貼到文檔或郵件里。整個過程沒有任何圖形界面但恰恰是這種“低科技”的做法讓周報這件事變成了一條可復(fù)用的命令而不是打開一個軟件找半天導(dǎo)出按鈕。5. 極簡工具的邊界什么時候該用 caveman什么時候別用5.1 誰適合用 caveman我觀察到的用戶畫像caveman 不是給所有人準備的。到目前為止我發(fā)現(xiàn)用它用得順手的主要是這幾類人開發(fā)者或重度命令行用戶不排斥終端甚至覺得終端比圖形界面更高效數(shù)據(jù)敏感者不希望自己的任務(wù)列表、工作日志存在第三方服務(wù)器上極簡主義實踐者意識到記錄的關(guān)鍵在于“快”功能復(fù)雜度反而妨礙記錄頻率有長期主義心態(tài)的人希望自己的記錄十年后依然可以用純文本打開而不是被困在一個停止維護的 App 里。如果你符合其中的一兩類用這類工具大概率會很順手。但反過來如果你不希望接觸命令行或者你更喜歡用手機隨手記錄那這個工具就不一定合適。5.2 caveman 不會做的事協(xié)作、提醒與復(fù)雜排期極簡工具的代價是它放棄了很多現(xiàn)代軟件的默認能力這一點必須說清楚。caveman不適合團隊協(xié)作。它沒有權(quán)限系統(tǒng)沒有通知機制沒有評論互動。同一個文件理論上可以多人編輯但沖突處理和同步協(xié)作都需要額外的手段這不是它擅長的事。如果你需要的是一個團隊項目管理系統(tǒng)Jira、Trello、飛書這類產(chǎn)品仍然是更好的選擇。caveman也沒有主動提醒能力。它不會在你忘記某個截止日期的時候彈通知因為它根本沒有后臺進程。所有提醒都必須靠外部機制實現(xiàn)比如系統(tǒng)定時任務(wù)發(fā)通知、或者你自己養(yǎng)成每天看兩次list的習(xí)慣。對提醒依賴很強的人這個工具會讓你失望。caveman更不適合做復(fù)雜排期。它每條任務(wù)只有一個簡單優(yōu)先級和一個日期不支持開始時間、截止時間、依賴關(guān)系、里程碑這些概念。它更像一個“第二大腦的速記本”而不是一個項目調(diào)度器。把甘特圖之類的需求往這個工具上套屬于用錯工具。5.3 “原始”的反思與后續(xù)可能的擴展做 caveman 的過程中我無數(shù)次被問到同一個問題“都什么年代了還用文本文件管任務(wù)”一開始我會解釋理由后來我發(fā)現(xiàn)自己也經(jīng)常思考這個問題的反面是不是我把極簡當(dāng)成了目的本身我的結(jié)論是極簡不是目的而是手段。caveman 讓我愿意記錄原因是它足夠快、足夠可靠、數(shù)據(jù)足夠可控。如果哪天有新的需求出現(xiàn)我完全不排斥給這個工具做擴展。目前我已經(jīng)在考慮幾個未來的方向給tasks.txt增加一個索引文件支持全文搜索加一個report子命令直接把周報導(dǎo)出成 Markdown 文件通過系統(tǒng)通知機制實現(xiàn)簡單的“每日摘要提醒”支持多文件數(shù)據(jù)源比如把工作和生活分成兩個 txt 文件。這些改動都不會破壞現(xiàn)有格式因為數(shù)據(jù)的根基是純文本程序怎么變都不會影響數(shù)據(jù)本身。這也是為什么我敢把這個工具“做舊”——因為文本這種“舊格式”恰恰是它最不容易過時的部分。我把這個項目陸續(xù)維護了快一年代碼量從最初的三百行變成現(xiàn)在的一千出頭但使用方式幾乎沒有變過。我最滿意的不是它有多少功能而是無論過了多久打開終端敲幾個字數(shù)據(jù)就在那里。最后再分享一個我一直在用的小技巧把cm add的調(diào)用封裝成一個系統(tǒng)級快捷鍵在任意窗口里按下快捷鍵彈出的輸入框直接寫任務(wù)描述回車后任務(wù)就進了 txt。這個動作從有想法到落盤不超過三秒鐘比解鎖手機打開 App 再新建任務(wù)快得多。如果你也被各種“功能過?!钡墓ぞ吒愕眯臒┎环翉倪@類原始但可靠的方案開始試試。工具是拿來用的不是拿來供的——這是我做 caveman 一年來最大的體會。