生活分享

实战:维护既有专案

建立现况基准,处理一项真实变更,以回归检查和交接纪录交付可追溯成果。

阅读时间约 15 分钟 · 操作 30 分钟

原创流程示意图,非产品界面截图。
图片:Mokaair (© Mokaair)
回总目录:Codex 学习中心:完整教程目录

进阶 · Desktop / CLI / VS Code / JetBrains / cloud

本篇目录
  1. 目标与起点
  2. 步骤 1:记录基准,不先要求重写
  3. 步骤 2:把范围写成可检查的契约
  4. 步骤 3:加入会先失败的新测试
  5. 步骤 4:实作并检查差异
  6. 步骤 5:回归画面与保存资料
  7. 还原与交接

目标与起点

本段提到的教学与资源: 练习材料 · ·

步骤 1:记录基准,不先要求重写

开启 maintenance-lab,应只有 index.html、style.css、app.js、core.mjs、core.test.mjs 五份程式档。先执行 node --test core.test.mjs,预期三项通过,再记下 Node 版本、日期与资料夹。如果不是这个结果,先查是否拿到 start 或 broken,不把既有失败算在这次重构。将副本纳入自己的本机 Git 基准提交,或另存完整 baseline 副本;保存后才开始修改,确保能还原这次改动。

修改前也要建立画面基准:在此资料夹执行下方「画面回归」列出的对应平台预览命令,使用专供练习的浏览器设定档与来源网址。先确认 Show 为 All tasks 且没有既存资料;若已有资料,先保存,再选用另一个全新练习设定档,以空白状态开始,不直接清除原资料。新增 Read、Build,完成 Read,确认 1 active / 2 total;在浏览器开发者工具的 Application/Storage → Local Storage 中,保存 mokaair-codex-todo-v1 的虚构 JSON,包含两笔 ID 与完成状态。保留这两笔资料、网址及浏览器设定档供修改后比对。接著在桌面版开启 maintenance-lab 并建立任务,或从此资料夹另开的终端机执行 codex,再提交下一步的需求。

请 Codex 唯读查看 app.js 中更新 #count 的地方,描述统计是全部任务还是筛选后清单。正确基准是全部任务,即使选 Completed,仍显示所有任务的 active/total。接著确认储存键 mokaair-codex-todo-v1、version 1、ID 与 completed 型别。重构只搬动计数责任,不改资料格式,不新增储存迁移。这个先读再改的步骤能避免把看似重复的程式码合并后改变原本细节。

步骤 2:把范围写成可检查的契约

项目本次要求不变条件
core.mjs新增纯函式 countTasks(tasks)不修改输入,不碰 DOM/储存
app.js呼叫 countTasks 更新 #count显示文字与全部任务计数不变
maintenance.test.mjs新增计数与不变性案例原 core.test.mjs 保留
HTML/CSS本次无修改需要按钮、版面、焦点样式维持
储存资料不做迁移原 key、version、ID 与状态保持

小范围不代表不验收;它让你能把新的失败和少数修改连起来。

步骤 3:加入会先失败的新测试

把下方内容另存为 maintenance.test.mjs,与原测试并列。先跑两份测试,新函式尚未存在时,新三项会失败而原三项仍通过;保存这个预期失败,确认测试真的会因缺少计数函式而报错。不要因红色结果就把教材当成坏版本,也不要让 Codex 删掉新测试来取得绿色。这里输入都符合既有 Task 契约,没有擅自扩大成任意资料清理工具。

maintenance.test.mjs · javascript
import test from 'node:test';
import assert from 'node:assert/strict';
import * as core from './core.mjs';

test('empty list counts are zero', () => {
  assert.deepEqual(core.countTasks([]), { active: 0, total: 0 });
});
test('count all tasks without deduplicating equal titles', () => {
  const tasks = [
    { id: 'a', title: 'Read', completed: false },
    { id: 'b', title: 'Read', completed: true },
    { id: 'c', title: 'Build', completed: false },
  ];
  assert.deepEqual(core.countTasks(tasks), { active: 2, total: 3 });
});
test('counting preserves frozen task data and storage compatibility', () => {
  const task = Object.freeze({ id: 'a', title: 'Read', completed: true });
  const tasks = Object.freeze([task]);
  const before = core.encodeTasks(tasks);
  assert.deepEqual(core.countTasks(tasks), { active: 0, total: 1 });
  assert.equal(core.encodeTasks(tasks), before);
  assert.deepEqual(core.decodeTasks(before), tasks);
});
各平台相同的回归命令 · sh
node --test core.test.mjs maintenance.test.mjs

步骤 4:实作并检查差异

受限重构提示词 · text
Add countTasks(tasks) to core.mjs, returning {active, total} for the full list.
Use it in app.js when updating #count; keep the existing visible text.
Preserve storage format, key, IDs, task behavior and original tests.
Do not edit index.html, style.css or the new test expectations.
Run node --test core.test.mjs maintenance.test.mjs.
Report changed files, test results and remaining browser verification.

完成后应有六项测试通过。参考核心函式如下,app.js 需汇入它并在 render 里先对 tasks 计数,再组出与原本相同的文字。特别检查是否误传 shown,也就是目前筛选结果;那会让 Completed 页面错显示总数。查看 git diff 或编辑器差异,原测试、HTML、CSS、储存读写不应被改动。纯函式的测试绿色,不能取代这个接线检查。

core.mjs 的参考新增函式 · javascript
export function countTasks(tasks) {
  return {
    active: tasks.filter((task) => !task.completed).length,
    total: tasks.length,
  };
}

在 app.js 原有的第一行汇入清单加上 countTasks,保留原六个函式。接著找到 render 中原本写入 #count.textContent 的那一行,用第二段替换;不要把整个 render 换掉,也不改前面产生 shown 的筛选。以下两段只是指定位置的片段,不是完整 app.js。

app.js:替换原本的 core 汇入行 · javascript
import { countTasks, addTask, toggleTask, removeTask, visibleTasks, decodeTasks, encodeTasks } from "./core.mjs";
app.js:替换 render 中原本的统计赋值 · javascript
const counts = countTasks(tasks);
document.querySelector("#count").textContent = `${counts.active} active / ${counts.total} total`;

检查这里的参数确实是 tasks。只有 Read 完成、Build 未完成时,Completed 的 shown 只有一笔;若误传 shown,会得到 0 active / 1 total,而正确画面应仍为 1 active / 2 total。核心六项测试只验 countTasks 的函式契约,仍可能全部通过,所以保留这个独立的接线与画面核对。

步骤 5:回归画面与保存资料

若修改前的同一个练习服务仍在执行,直接重新整理该网址,不另启动第二个;已停止才使用以下命令。在此资料夹启动预览:Windows 执行 py -3 -m http.server 4173 --bind 127.0.0.1;macOS/Linux 执行 python3 -m http.server 4173 --bind 127.0.0.1,浏览同一 http://127.0.0.1:4173。若已有人占用,先核对原服务,不直接终止它。在修改前后使用同一来源网址及虚构待办,避免换连接埠后空白的 localStorage 被误认为资料遗失。保留修改前建立的 Read、Build,不重设或重复新增;先比对储存的两笔 ID 与完成状态,再依序选 All tasks、Active、Completed,三次统计都必须是 1 active / 2 total。

重新整理确认两笔资料、ID 与完成状态保留。先用同一组两笔资料,在 390px 与桌面宽度各检查一次筛选与统计;接著选 All tasks,只删除一次 Read,确认 1 active / 1 total。按 Tab 确认焦点仍可见。这是回归验收,不需要新的设计图。若数字随筛选改变,就回到 app.js 查函式引数;若纯函式本身不对,用新测试的实际失败案例定位。只有在确实观察过后才填写浏览器通过,未操作的系统另外标记。

还原与交接

需要撤回时先保存当前差异,再只还原本次改动的 core.mjs、app.js,并移走自己新增的 maintenance.test.mjs;重新执行原本三项测试及统计划面,应回到原基准。不要用整个专案强制重设来清掉其他人的修改。交接写明基准、抽出的责任、未变的资料契约、六项测试、实际画面验收及还原位置;没跑的测试不能写通过。下一个维护需求另列范围,参考,不要在这次小重构顺便重写储存层或更换框架。 完成后在自己的预览终端机按 Ctrl+C 停止这次服务,保留基准 JSON 与验收纪录。

原创流程示意图,非产品界面截图。
原创流程示意图,非产品界面截图。 · 图片:Mokaair (© Mokaair)
阅读完整文字说明

Three numbered stages: identify the starting point, perform the exercise, and verify the result. Original illustration, not a product screenshot.

回总目录

  • 生活分享

    Codex 学习中心:完整教程目录

    从安装、第一个任务到 MD 规则与进阶集成,规划 60 篇 Codex 教程、十个单元。按程度、平台、需求或命令搜索下一篇;尚未公开的教程会标示状态,方便安排学习路线。

  • 生活分享

    Worktree 与多任务隔离

    Worktree 让同一个 Git 程式库有不同的工作目录,各自承接不同分支。它适合让两项工作分开改档,但资料库、连接埠与外部服务仍可能共用,不能把档案隔离当成所有资源隔离。

  • 生活分享

    实战:制作小网站

    从 brief.md 规划并制作 Small Steps 待办网站,完成新增、完成、删除、筛选与本机资料保存。将 HTML、CSS、资料函式、画面事件与测试分开,以 Node 测试和浏览器操作验收,并留下可重新启动与还原的交接纪录。

  • 生活分享

    用量与效率:减少重工

    记录任务条件、模型选项、时间与成果,找出能减少无效重试和过多上下文的调整。

最新旅游情报攻略

资料来源

生活分享