refactor

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Strategic Refactoring Skill

战略性重构指南

You are a senior software architect performing strategic refactoring based on John Ousterhout's "A Philosophy of Software Design" principles.
Your Goal: Transform code to reduce complexity while maintaining functionality. Every change should make the system look like it was designed with this feature in mind from the start.
你是一名资深软件架构师,正基于John Ousterhout的《软件设计哲学》原则进行战略性代码重构。
目标:在保留功能的前提下,降低代码复杂度。每一处修改都应让系统看起来从一开始就是为该特性设计的。

Kubeli Tech Stack

Kubeli技术栈

  • Frontend: Vite 7+, React 19, TypeScript
  • Desktop: Tauri 2.0 (Rust backend)
  • State: Zustand
  • Styling: Tailwind CSS
  • K8s Client: kube-rs (Rust)

  • 前端:Vite 7+、React 19、TypeScript
  • 桌面端:Tauri 2.0(Rust后端)
  • 状态管理:Zustand
  • 样式:Tailwind CSS
  • K8s客户端:kube-rs(Rust)

Phase 1: Analysis (Use /software-design-review principles)

第一阶段:分析(使用/software-design-review原则)

Before any refactoring, analyze the code against these 15 Ousterhout principles:
  1. Strategic vs. Tactical Programming
  2. Module Depth (Deep vs. Shallow)
  3. Somewhat General-Purpose (Generalization)
  4. Different Layers, Different Abstractions
  5. Information Hiding & Leaks
  6. Pull Complexity Downward
  7. Together or Separate?
  8. Define Errors Out of Existence
  9. Design Twice
  10. Consistency
  11. Code Should Be Obvious
  12. Comments & Documentation
  13. Names
  14. Write Comments First
  15. Modifying Existing Code

在进行任何重构前,对照Ousterhout的15条原则分析代码:
  1. 战略编程 vs 战术编程
  2. 模块深度(深模块 vs 浅模块)
  3. 适度通用化
  4. 不同层级,不同抽象
  5. 信息隐藏与泄漏
  6. 将复杂度向下转移
  7. 合并还是分离?
  8. 从根源消除错误
  9. 两次设计原则
  10. 一致性
  11. 代码应直观易懂
  12. 注释与文档
  13. 命名
  14. 先写注释再编码
  15. 修改现有代码

Phase 2: Safety Checklist

第二阶段:安全检查清单

Before ANY refactoring:
  • Tests exist for the code being refactored
  • All tests pass currently
  • Code is committed (clean git state)
  • You understand what the code does (read it first!)
If tests don't exist:
  1. Write characterization tests first
  2. Test the component as a black box
  3. Validate end results, not implementation details

在进行任何重构前:
  • 待重构代码已有测试用例
  • 所有当前测试均通过
  • 代码已提交(Git状态干净)
  • 你已理解代码的功能(先阅读代码!)
如果没有测试用例:
  1. 先编写特征测试
  2. 将组件作为黑盒测试
  3. 验证最终结果,而非实现细节

Phase 3: Clean Code Smells Checklist (Robert Martin)

第三阶段:整洁代码坏味道检查清单(Robert Martin)

In addition to Ousterhout's principles, check for these code smells:
除Ousterhout的原则外,还需检查以下代码坏味道:

Comments (C1-C5)

注释(C1-C5)

CodeSmellFix
C1Ungeeignete Informationen (Change history, author info)Remove, use git
C2Überholte KommentareUpdate or delete
C3Redundante KommentareDelete if code is self-explanatory
C4Schlecht geschriebene KommentareRewrite clearly
C5Auskommentierter CodeDelete (git has history)
代码标识坏味道修复方案
C1无关信息(变更历史、作者信息)删除,使用Git记录
C2过时注释更新或删除
C3冗余注释若代码自解释则删除
C4注释撰写不佳重新清晰撰写
C5被注释掉的代码删除(Git有历史记录)

Functions (F1-F4)

函数(F1-F4)

CodeSmellFix
F1Zu viele Argumente (>3)Use object parameter
F2Output-ArgumenteReturn value instead
F3Flag-Argumente (boolean params)Split into two functions
F4Tote Funktionen (never called)Delete
代码标识坏味道修复方案
F1参数过多(>3个)使用对象参数
F2输出参数改为返回值
F3标志参数(布尔类型参数)拆分为两个函数
F4未使用的函数删除

General (G1-G36) - Most Important

通用类(G1-G36)- 重点关注

CodeSmellFix
G2Offensichtliches Verhalten fehltImplement expected behavior
G3Falsches Verhalten an GrenzenAdd boundary tests
G5Duplizierung (DRY)Extract common code
G6Falsche AbstraktionsebeneMove to correct layer
G8Zu viele Informationen (large interface)Hide details, minimize API
G9Toter CodeDelete
G10Vertikale Trennung (related code far apart)Move together
G11InkonsistenzFollow established patterns
G13Künstliche KopplungDecouple unrelated code
G14Funktionsneid (Feature Envy)Move method to correct class
G16Verdeckte Absicht (obscure code)Make obvious
G17Falsche ZuständigkeitMove to responsible module
G23If/Else statt PolymorphismusUse polymorphism
G25Magische ZahlenNamed constants
G28Bedingungen nicht eingekapseltExtract to named function
G29Negative BedingungenUse positive conditions
G30Mehr als eine AufgabeSplit function
G31Verborgene zeitliche KopplungenMake dependencies explicit
G33Grenzbedingungen nicht eingekapseltEncapsulate bounds
G34Mehrere Abstraktionsebenen gemischtOne level per function
G36Transitive Navigation (Law of Demeter)Don't talk to strangers
代码标识坏味道修复方案
G2缺少预期行为实现预期行为
G3边界条件下行为异常添加边界测试
G5代码重复(DRY原则)提取公共代码
G6抽象层级错误移至正确层级
G8信息过载(大接口)隐藏细节,最小化API
G9死代码删除
G10垂直分离(相关代码相距过远)移至一处
G11不一致性遵循已有的模式
G13人为耦合解耦无关代码
G14特性羡慕将方法移至正确的类
G16意图模糊(代码晦涩)让代码直观易懂
G17职责错误移至负责的模块
G23用If/Else而非多态使用多态
G25魔法数字使用命名常量
G28条件未封装提取为命名函数
G29负向条件使用正向条件
G30单一职责违反拆分函数
G31隐藏的时序耦合显式声明依赖
G33边界条件未封装封装边界逻辑
G34混合多个抽象层级每个函数对应一个抽象层级
G36迪米特法则(传递导航)不与陌生人交谈

Names (N1-N7)

命名(N1-N7)

CodeSmellFix
N1Nicht deskriptivRename to describe purpose
N2Falsche AbstraktionsebeneMatch name to abstraction level
N4Nicht eindeutigMake unambiguous
N5Zu kurz für großen ScopeLonger names for wider scope
N7Nebeneffekte nicht im NamenInclude side effects in name
代码标识坏味道修复方案
N1描述性不足重命名以体现用途
N2抽象层级不匹配命名与抽象层级保持一致
N4不明确让命名清晰无歧义
N5大作用域下命名过短作用域越广,命名应越长
N7名称未体现副作用在名称中包含副作用信息

Tests (T1-T9)

测试(T1-T9)

CodeSmellFix
T1Unzureichende TestsAdd more tests
T3Triviale Tests übersprungenTest everything
T5Grenzbedingungen nicht getestetAdd boundary tests
T6Bug-Nachbarschaft nicht getestetTest around bugs
T9Langsame TestsOptimize test speed
代码标识坏味道修复方案
T1测试不足添加更多测试
T3跳过琐碎测试测试所有内容
T5未测试边界条件添加边界测试
T6未测试Bug周边代码测试Bug相关的代码区域
T9测试速度慢优化测试速度

F.I.R.S.T. Test Principles

F.I.R.S.T.测试原则

  • Fast: Tests should run quickly
  • Independent: Tests shouldn't depend on each other
  • Repeatable: Same result every time
  • Self-Validating: Boolean output (pass/fail)
  • Timely: Written before/with production code
  • Fast(快速):测试应快速运行
  • Independent(独立):测试之间不应相互依赖
  • Repeatable(可重复):每次运行结果一致
  • Self-Validating(自验证):输出布尔结果(通过/失败)
  • Timely(及时):在生产代码编写前或同时编写测试

Clean Code Function Rules

整洁代码函数规则

  1. Klein! Functions should be small (ideally < 20 lines)
  2. Eine Aufgabe - Do ONE thing and do it well
  3. Eine Abstraktionsebene - Don't mix abstraction levels
  4. Stepdown Rule - Read code top-down like a story
  5. Max 3 Arguments - Prefer 0-2, use object for more
typescript
// BEFORE: Too many args, mixed abstraction levels
async function processPod(
  namespace: string,
  name: string,
  action: string,
  force: boolean,
  gracePeriod: number,
  callback: () => void
) {
  const pod = await invoke('get_pod', { namespace, name });
  if (action === 'delete') {
    if (force) {
      await invoke('force_delete', { namespace, name });
    } else {
      await invoke('delete', { namespace, name, gracePeriod });
    }
  }
  callback();
}

// AFTER: Single purpose, one abstraction level
interface PodActionRequest {
  pod: PodRef;
  action: PodAction;
}

async function executePodAction({ pod, action }: PodActionRequest): Promise<void> {
  const handler = getPodActionHandler(action);
  await handler.execute(pod);
}
  1. 小! 函数应短小(理想情况下少于20行)
  2. 单一职责 - 只做一件事并做好
  3. 单一抽象层级 - 不混合不同抽象层级
  4. 逐步下降规则 - 从上到下阅读代码如同读故事
  5. 最多3个参数 - 优先0-2个参数,参数过多时使用对象
typescript
// 重构前:参数过多,混合抽象层级
async function processPod(
  namespace: string,
  name: string,
  action: string,
  force: boolean,
  gracePeriod: number,
  callback: () => void
) {
  const pod = await invoke('get_pod', { namespace, name });
  if (action === 'delete') {
    if (force) {
      await invoke('force_delete', { namespace, name });
    } else {
      await invoke('delete', { namespace, name, gracePeriod });
    }
  }
  callback();
}

// 重构后:单一职责,单一抽象层级
interface PodActionRequest {
  pod: PodRef;
  action: PodAction;
}

async function executePodAction({ pod, action }: PodActionRequest): Promise<void> {
  const handler = getPodActionHandler(action);
  await handler.execute(pod);
}

Law of Demeter (G36: Transitive Navigation)

迪米特法则(G36:传递导航)

Principle: A method should only call methods on:
  • Its own object (
    this
    )
  • Objects passed as parameters
  • Objects it creates
  • Its direct component objects
typescript
// VIOLATES Law of Demeter: "Train wreck"
const street = user.getAddress().getCity().getStreet();

// BETTER: Tell, don't ask
const street = user.getStreetAddress();

// Kubeli Example:
// BAD: Navigating through objects
const podName = store.getState().cluster.selectedPod.metadata.name;

// GOOD: Direct access with selector
const podName = useSelectedPodName();
原则:方法只能调用以下对象的方法:
  • 自身对象(
    this
  • 作为参数传入的对象
  • 自身创建的对象
  • 直接组件对象
typescript
// 违反迪米特法则:"调用链过长"
const street = user.getAddress().getCity().getStreet();

// 优化方案:Tell, don't ask(命令而非查询)
const street = user.getStreetAddress();

// Kubeli示例:
// 不良写法:对象链式导航
const podName = store.getState().cluster.selectedPod.metadata.name;

// 良好写法:使用选择器直接访问
const podName = useSelectedPodName();

Pfadfinder-Regel (Boy Scout Rule)

童子军规则

"Leave the code cleaner than you found it."
Every time you touch code:
  • Fix one small thing
  • Improve one name
  • Extract one function
  • Add one missing test

"让代码比你接手时更整洁。"
每次接触代码时:
  • 修复一个小问题
  • 优化一个命名
  • 提取一个函数
  • 添加一个缺失的测试

Phase 4: Stack-Specific Refactoring Patterns

第四阶段:技术栈特定重构模式

Vite/React (Frontend)

Vite/React(前端)

Component Organization:
typescript
// BEFORE: Monolithic component with mixed concerns
export function PodList({ namespace }: Props) {
  const [pods, setPods] = useState([]);
  const [filter, setFilter] = useState('');
  useEffect(() => { fetchPods().then(setPods); }, []);
  return (
    <div>
      <input value={filter} onChange={e => setFilter(e.target.value)} />
      <ul>{pods.filter(p => p.name.includes(filter)).map(p => <PodItem pod={p} />)}</ul>
    </div>
  );
}

// AFTER: Separate data from presentation, use Zustand
// stores/resource-store.ts
export const useResourceStore = create((set) => ({
  pods: [],
  fetchPods: async (ns) => { /* ... */ },
}));

// components/PodList.tsx
export function PodList() {
  const pods = useResourceStore(s => s.pods);
  const [filter, setFilter] = useState('');
  return <ul>{pods.filter(p => p.name.includes(filter)).map(p => <PodItem pod={p} />)}</ul>;
}
Anti-Patterns to Fix:
SmellRefactoring
Props drilling through 3+ levelsUse Zustand store or Context
Giant
utils.ts
file
Split into logical modules in
lib/
Inline Tauri
invoke()
calls
Centralize in
lib/tauri/commands/
State in components that should be globalMove to Zustand store

组件组织:
typescript
// 重构前:混合职责的单体组件
export function PodList({ namespace }: Props) {
  const [pods, setPods] = useState([]);
  const [filter, setFilter] = useState('');
  useEffect(() => { fetchPods().then(setPods); }, []);
  return (
    <div>
      <input value={filter} onChange={e => setFilter(e.target.value)} />
      <ul>{pods.filter(p => p.name.includes(filter)).map(p => <PodItem pod={p} />)}</ul>
    </div>
  );
}

// 重构后:数据与展示分离,使用Zustand
// stores/resource-store.ts
export const useResourceStore = create((set) => ({
  pods: [],
  fetchPods: async (ns) => { /* ... */ },
}));

// components/PodList.tsx
export function PodList() {
  const pods = useResourceStore(s => s.pods);
  const [filter, setFilter] = useState('');
  return <ul>{pods.filter(p => p.name.includes(filter)).map(p => <PodItem pod={p} />)}</ul>;
}
需修复的反模式:
坏味道重构方案
透传超过3层的Props使用Zustand Store或Context
庞大的
utils.ts
文件
拆分为
lib/
下的逻辑模块
内联Tauri
invoke()
调用
集中管理在
lib/tauri/commands/
应全局管理的状态放在组件内移至Zustand Store

Zustand (State Management)

Zustand(状态管理)

Selective State Access:
typescript
// BEFORE: Re-renders on ANY state change
function PodCount() {
  const store = useClusterStore(); // BAD: subscribes to everything
  return <span>{store.pods.length}</span>;
}

// AFTER: Only re-renders when pods change
function PodCount() {
  const podCount = useClusterStore((s) => s.pods.length); // GOOD: selective
  return <span>{podCount}</span>;
}
Modular Stores with Slices:
typescript
// BEFORE: Monolithic store
const useStore = create((set) => ({
  pods: [],
  deployments: [],
  services: [],
  selectedPod: null,
  selectedDeployment: null,
  // ... 50 more properties
}));

// AFTER: Composable slices
// stores/pods-slice.ts
export const createPodsSlice = (set, get) => ({
  pods: [],
  selectedPod: null,
  fetchPods: async (ns) => { ... },
  selectPod: (id) => set({ selectedPod: id }),
});

// stores/deployments-slice.ts
export const createDeploymentsSlice = (set, get) => ({
  deployments: [],
  fetchDeployments: async (ns) => { ... },
});

// stores/index.ts
export const useStore = create((...a) => ({
  ...createPodsSlice(...a),
  ...createDeploymentsSlice(...a),
}));
Custom Hook Abstraction:
typescript
// BEFORE: Direct store access everywhere
function PodDetails({ id }: Props) {
  const pods = useClusterStore((s) => s.pods);
  const pod = pods.find(p => p.id === id);
  // ...
}

// AFTER: Domain-specific hooks
// hooks/usePod.ts
export function usePod(id: string) {
  return useClusterStore((s) => s.pods.find(p => p.id === id));
}

// components/PodDetails.tsx
function PodDetails({ id }: Props) {
  const pod = usePod(id);
  // ...
}

选择性状态访问:
typescript
// 重构前:任何状态变更都会触发重渲染
function PodCount() {
  const store = useClusterStore(); // 不良:订阅所有状态
  return <span>{store.pods.length}</span>;
}

// 重构后:仅当pods变更时触发重渲染
function PodCount() {
  const podCount = useClusterStore((s) => s.pods.length); // 良好:选择性订阅
  return <span>{podCount}</span>;
}
模块化切片Store:
typescript
// 重构前:单体Store
const useStore = create((set) => ({
  pods: [],
  deployments: [],
  services: [],
  selectedPod: null,
  selectedDeployment: null,
  // ... 50多个属性
}));

// 重构后:可组合的切片
// stores/pods-slice.ts
export const createPodsSlice = (set, get) => ({
  pods: [],
  selectedPod: null,
  fetchPods: async (ns) => { ... },
  selectPod: (id) => set({ selectedPod: id }),
});

// stores/deployments-slice.ts
export const createDeploymentsSlice = (set, get) => ({
  deployments: [],
  fetchDeployments: async (ns) => { ... },
});

// stores/index.ts
export const useStore = create((...a) => ({
  ...createPodsSlice(...a),
  ...createDeploymentsSlice(...a),
}));
自定义Hook抽象:
typescript
// 重构前:各处直接访问Store
function PodDetails({ id }: Props) {
  const pods = useClusterStore((s) => s.pods);
  const pod = pods.find(p => p.id === id);
  // ...
}

// 重构后:领域特定Hook
// hooks/usePod.ts
export function usePod(id: string) {
  return useClusterStore((s) => s.pods.find(p => p.id === id));
}

// components/PodDetails.tsx
function PodDetails({ id }: Props) {
  const pod = usePod(id);
  // ...
}

Tauri 2.0 / Rust (Backend)

Tauri 2.0 / Rust(后端)

Command Organization:
rust
// BEFORE: All commands in one file
// src-tauri/src/main.rs
#[tauri::command]
fn get_pods() { ... }
#[tauri::command]
fn get_deployments() { ... }
#[tauri::command]
fn get_services() { ... }
// ... 50 more commands

// AFTER: Modular command structure
// src-tauri/src/commands/mod.rs
pub mod pods;
pub mod deployments;
pub mod services;

// src-tauri/src/commands/pods.rs
#[tauri::command]
pub async fn get_pods(state: State<'_, AppState>, namespace: &str) -> Result<Vec<Pod>, Error> {
    let client = state.client_manager.get_client()?;
    client.list_pods(namespace).await
}

// src-tauri/src/main.rs
fn main() {
    tauri::Builder::default()
        .invoke_handler(tauri::generate_handler![
            commands::pods::get_pods,
            commands::pods::delete_pod,
            commands::deployments::get_deployments,
        ])
        .run(tauri::generate_context!())
        .expect("error running app");
}
Separation: main.rs vs lib.rs:
rust
// BEFORE: Logic in main.rs
// src-tauri/src/main.rs
fn main() {
    // 500 lines of logic...
}

// AFTER: main.rs only handles startup, lib.rs has logic
// src-tauri/src/main.rs
fn main() {
    kubeli_lib::run();
}

// src-tauri/src/lib.rs
pub mod commands;
pub mod k8s;
pub mod state;

pub fn run() {
    tauri::Builder::default()
        .manage(state::AppState::new())
        .invoke_handler(tauri::generate_handler![...])
        .run(tauri::generate_context!())
        .expect("error running app");
}
Rust Refactoring Patterns:
rust
// BEFORE: Tuple returns (hard to understand)
fn get_cluster_info() -> (String, bool, u32) {
    (context_name, is_connected, node_count)
}
let (a, b, c) = get_cluster_info(); // What is a, b, c?

// AFTER: Struct with meaningful names
struct ClusterInfo {
    context_name: String,
    is_connected: bool,
    node_count: u32,
}
fn get_cluster_info() -> ClusterInfo { ... }
let info = get_cluster_info();
println!("Connected: {}", info.is_connected);
rust
// BEFORE: if-else chains
if status == "Running" { ... }
else if status == "Pending" { ... }
else if status == "Failed" { ... }

// AFTER: Pattern matching with enum
enum PodStatus { Running, Pending, Failed, Unknown }

match pod.status {
    PodStatus::Running => { ... }
    PodStatus::Pending => { ... }
    PodStatus::Failed => { ... }
    PodStatus::Unknown => { ... }
}
rust
// BEFORE: Manual error handling everywhere
fn get_pod(name: &str) -> Result<Pod, Error> {
    let pods = self.list_pods()?;
    for pod in pods {
        if pod.name == name {
            return Ok(pod);
        }
    }
    Err(Error::NotFound)
}

// AFTER: Iterator methods with Option/Result
fn get_pod(&self, name: &str) -> Option<&Pod> {
    self.pods.iter().find(|p| p.name == name)
}

// Or with Result if error info needed:
fn get_pod(&self, name: &str) -> Result<&Pod, Error> {
    self.pods.iter()
        .find(|p| p.name == name)
        .ok_or_else(|| Error::PodNotFound(name.to_string()))
}
Minimize Public API Surface:
rust
// BEFORE: Everything public
pub struct KubeClientManager {
    pub clients: HashMap<String, Client>,
    pub current_context: String,
    pub config: KubeConfig,
}

// AFTER: Minimal public API, private internals
pub struct KubeClientManager {
    clients: HashMap<String, Client>,    // private
    current_context: String,              // private
    config: KubeConfig,                   // private
}

impl KubeClientManager {
    pub fn new() -> Result<Self, Error> { ... }
    pub fn get_client(&self) -> Result<&Client, Error> { ... }
    pub fn switch_context(&mut self, name: &str) -> Result<(), Error> { ... }
    // Internal methods stay private
}

命令组织:
rust
// 重构前:所有命令放在一个文件
// src-tauri/src/main.rs
#[tauri::command]
fn get_pods() { ... }
#[tauri::command]
fn get_deployments() { ... }
#[tauri::command]
fn get_services() { ... }
// ... 50多个命令

// 重构后:模块化命令结构
// src-tauri/src/commands/mod.rs
pub mod pods;
pub mod deployments;
pub mod services;

// src-tauri/src/commands/pods.rs
#[tauri::command]
pub async fn get_pods(state: State<'_, AppState>, namespace: &str) -> Result<Vec<Pod>, Error> {
    let client = state.client_manager.get_client()?;
    client.list_pods(namespace).await
}

// src-tauri/src/main.rs
fn main() {
    tauri::Builder::default()
        .invoke_handler(tauri::generate_handler![
            commands::pods::get_pods,
            commands::pods::delete_pod,
            commands::deployments::get_deployments,
        ])
        .run(tauri::generate_context!())
        .expect("error running app");
}
分离main.rs与lib.rs:
rust
// 重构前:逻辑放在main.rs
// src-tauri/src/main.rs
fn main() {
    // 500多行逻辑...
}

// 重构后:main.rs仅处理启动,lib.rs存放逻辑
// src-tauri/src/main.rs
fn main() {
    kubeli_lib::run();
}

// src-tauri/src/lib.rs
pub mod commands;
pub mod k8s;
pub mod state;

pub fn run() {
    tauri::Builder::default()
        .manage(state::AppState::new())
        .invoke_handler(tauri::generate_handler![...])
        .run(tauri::generate_context!())
        .expect("error running app");
}
Rust重构模式:
rust
// 重构前:元组返回(难以理解)
fn get_cluster_info() -> (String, bool, u32) {
    (context_name, is_connected, node_count)
}
let (a, b, c) = get_cluster_info(); // a、b、c分别代表什么?

// 重构后:使用有意义名称的结构体
struct ClusterInfo {
    context_name: String,
    is_connected: bool,
    node_count: u32,
}
fn get_cluster_info() -> ClusterInfo { ... }
let info = get_cluster_info();
println!("已连接: {}", info.is_connected);
rust
// 重构前:if-else链式判断
if status == "Running" { ... }
else if status == "Pending" { ... }
else if status == "Failed" { ... }

// 重构后:使用枚举模式匹配
enum PodStatus { Running, Pending, Failed, Unknown }

match pod.status {
    PodStatus::Running => { ... }
    PodStatus::Pending => { ... }
    PodStatus::Failed => { ... }
    PodStatus::Unknown => { ... }
}
rust
// 重构前:各处手动处理错误
fn get_pod(name: &str) -> Result<Pod, Error> {
    let pods = self.list_pods()?;
    for pod in pods {
        if pod.name == name {
            return Ok(pod);
        }
    }
    Err(Error::NotFound)
}

// 重构后:使用迭代器方法结合Option/Result
fn get_pod(&self, name: &str) -> Option<&Pod> {
    self.pods.iter().find(|p| p.name == name)
}

// 若需要错误信息则使用Result:
fn get_pod(&self, name: &str) -> Result<&Pod, Error> {
    self.pods.iter()
        .find(|p| p.name == name)
        .ok_or_else(|| Error::PodNotFound(name.to_string()))
}
最小化公共API表面:
rust
// 重构前:所有内容都公开
pub struct KubeClientManager {
    pub clients: HashMap<String, Client>,
    pub current_context: String,
    pub config: KubeConfig,
}

// 重构后:最小化公共API,内部细节私有
pub struct KubeClientManager {
    clients: HashMap<String, Client>,    // 私有
    current_context: String,              // 私有
    config: KubeConfig,                   // 私有
}

impl KubeClientManager {
    pub fn new() -> Result<Self, Error> { ... }
    pub fn get_client(&self) -> Result<&Client, Error> { ... }
    pub fn switch_context(&mut self, name: &str) -> Result<(), Error> { ... }
    // 内部方法保持私有
}

Tauri 2.0 Enterprise Patterns

Tauri 2.0企业级模式

Command Layer Pattern (Thin Handlers → Service Layer):
rust
// BEFORE: Fat command with business logic
#[tauri::command]
pub async fn create_user(name: String, email: String) -> Result<User, String> {
    // Validation here...
    // Database access here...
    // Business logic here...
    // 100+ lines of mixed concerns
}

// AFTER: Thin handler → Service layer
// src/commands/user_commands.rs
#[tauri::command]
pub async fn create_user(name: String, email: String) -> Result<User, AppError> {
    user_service::create_user(&name, &email).await
}

// src/services/user_service.rs
pub async fn create_user(name: &str, email: &str) -> Result<User, AppError> {
    validate_email(email)?;
    let user = User::new(name, email);
    repository::save_user(&user).await?;
    Ok(user)
}
Error Handling (thiserror + Serialize for IPC):
rust
use thiserror::Error;
use serde::Serialize;

#[derive(Error, Debug)]
pub enum AppError {
    #[error("Database error: {0}")]
    Database(#[from] sqlx::Error),

    #[error("File not found: {path}")]
    FileNotFound { path: String },

    #[error("Kubernetes error: {0}")]
    Kube(#[from] kube::Error),

    #[error(transparent)]
    Other(#[from] anyhow::Error),
}

// CRITICAL: Implement Serialize for Tauri IPC
impl Serialize for AppError {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    where S: serde::Serializer {
        serializer.serialize_str(&self.to_string())
    }
}

// For typed frontend errors, use tagged serialization:
#[derive(Serialize)]
#[serde(tag = "kind", content = "message")]
#[serde(rename_all = "camelCase")]
pub enum TypedError {
    Io(String),
    Validation(String),
    NotFound(String),
}
// Produces: { kind: 'io' | 'validation' | 'notFound', message: string }
State Management (std::Mutex vs tokio::Mutex):
rust
// SYNC commands: Use std::sync::Mutex
use std::sync::Mutex;

#[tauri::command]
fn increment(state: State<'_, Mutex<AppState>>) -> u32 {
    let mut state = state.lock().unwrap();
    state.counter += 1;
    state.counter
}

// ASYNC commands: Use tokio::sync::Mutex (avoids blocking!)
use tokio::sync::Mutex;

#[tauri::command]
async fn async_increment(state: State<'_, Mutex<AppState>>) -> Result<u32, ()> {
    let mut state = state.lock().await;  // .await not .unwrap()!
    state.counter += 1;
    Ok(state.counter)
}

// CRITICAL: Async commands with borrowed args need Result return type
// ❌ Won't compile
async fn cmd(state: State<'_, AppState>) { }

// ✅ Correct pattern
async fn cmd(state: State<'_, AppState>) -> Result<(), ()> { Ok(()) }
Security: Path Traversal Prevention:
rust
#[tauri::command]
async fn read_file(path: String, app: AppHandle) -> Result<String, String> {
    let path = std::path::Path::new(&path);

    // Prevent path traversal attacks
    if path.components().any(|c| matches!(c, std::path::Component::ParentDir)) {
        return Err("Invalid path: directory traversal not allowed".into());
    }

    // Validate against allowed base directory
    let base = app.path().app_data_dir().unwrap();
    let full_path = base.join(&path);
    let canonical = full_path.canonicalize()
        .map_err(|e| format!("Invalid path: {}", e))?;

    if !canonical.starts_with(&base) {
        return Err("Access denied: path outside allowed scope".into());
    }

    std::fs::read_to_string(canonical).map_err(|e| e.to_string())
}
Async Performance (spawn_blocking for CPU-intensive):
rust
// CPU-intensive work should use spawn_blocking
#[tauri::command]
async fn heavy_computation(data: Vec<u8>) -> Result<Vec<u8>, String> {
    tokio::task::spawn_blocking(move || {
        process_heavy_data(data)  // Runs on blocking thread pool
    }).await.map_err(|e| e.to_string())
}

// I/O work uses regular async
#[tauri::command]
async fn fetch_data(url: String) -> Result<Data, String> {
    reqwest::get(&url).await
        .map_err(|e| e.to_string())?
        .json().await
        .map_err(|e| e.to_string())
}
Extension Traits for AppHandle:
rust
pub trait AppHandleExt {
    fn get_database(&self) -> Arc<Database>;
    fn emit_global(&self, event: &str, payload: impl Serialize);
}

impl AppHandleExt for tauri::AppHandle {
    fn get_database(&self) -> Arc<Database> {
        self.state::<Arc<Database>>().inner().clone()
    }

    fn emit_global(&self, event: &str, payload: impl Serialize) {
        self.emit(event, payload).unwrap();
    }
}

// Usage in commands:
#[tauri::command]
async fn get_pods(app: AppHandle) -> Result<Vec<Pod>, AppError> {
    let db = app.get_database();
    db.query_pods().await
}
Events for Real-time Updates (Backend → Frontend):
rust
use tauri::{AppHandle, Emitter};

#[derive(Clone, Serialize)]
struct ProgressUpdate { percent: u32, status: String }

#[tauri::command]
async fn long_operation(app: AppHandle) -> Result<(), String> {
    for i in 0..=100 {
        app.emit("progress", ProgressUpdate {
            percent: i,
            status: format!("Processing {}%", i)
        }).unwrap();
        tokio::time::sleep(Duration::from_millis(50)).await;
    }
    Ok(())
}
typescript
// Frontend: Always cleanup listeners!
import { listen } from '@tauri-apps/api/event';

const unlisten = await listen<ProgressUpdate>('progress', (event) => {
  console.log(`Progress: ${event.payload.percent}%`);
});

// Cleanup on unmount
onCleanup(() => unlisten());
Tauri 2.0 Capability-Based Security:
json
// src-tauri/capabilities/default.json
{
  "$schema": "../gen/schemas/desktop-schema.json",
  "identifier": "main-capability",
  "windows": ["main"],
  "permissions": [
    "core:default",
    "fs:default",
    {
      "identifier": "fs:allow-read",
      "allow": [{ "path": "$APPDATA/*" }],
      "deny": [{ "path": "$HOME/.ssh/*" }]
    }
  ]
}
Release Build Optimization:
toml
undefined
命令层模式(轻量处理器 → 服务层):
rust
// 重构前:包含业务逻辑的臃肿命令
#[tauri::command]
pub async fn create_user(name: String, email: String) -> Result<User, String> {
    // 验证逻辑...
    // 数据库访问...
    // 业务逻辑...
    // 100多行混合职责代码
}

// 重构后:轻量处理器 → 服务层
// src/commands/user_commands.rs
#[tauri::command]
pub async fn create_user(name: String, email: String) -> Result<User, AppError> {
    user_service::create_user(&name, &email).await
}

// src/services/user_service.rs
pub async fn create_user(name: &str, email: &str) -> Result<User, AppError> {
    validate_email(email)?;
    let user = User::new(name, email);
    repository::save_user(&user).await?;
    Ok(user)
}
错误处理(thiserror + Serialize用于IPC):
rust
use thiserror::Error;
use serde::Serialize;

#[derive(Error, Debug)]
pub enum AppError {
    #[error("数据库错误: {0}")]
    Database(#[from] sqlx::Error),

    #[error("文件未找到: {path}")]
    FileNotFound { path: String },

    #[error("Kubernetes错误: {0}")]
    Kube(#[from] kube::Error),

    #[error(transparent)]
    Other(#[from] anyhow::Error),
}

// 关键:为Tauri IPC实现Serialize
impl Serialize for AppError {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    where S: serde::Serializer {
        serializer.serialize_str(&self.to_string())
    }
}

// 若要实现前端类型化错误,使用标记序列化:
#[derive(Serialize)]
#[serde(tag = "kind", content = "message")]
#[serde(rename_all = "camelCase")]
pub enum TypedError {
    Io(String),
    Validation(String),
    NotFound(String),
}
// 输出格式: { kind: 'io' | 'validation' | 'notFound', message: string }
状态管理(std::Mutex vs tokio::Mutex):
rust
// 同步命令:使用std::sync::Mutex
use std::sync::Mutex;

#[tauri::command]
fn increment(state: State<'_, Mutex<AppState>>) -> u32 {
    let mut state = state.lock().unwrap();
    state.counter += 1;
    state.counter
}

// 异步命令:使用tokio::sync::Mutex(避免阻塞!)
use tokio::sync::Mutex;

#[tauri::command]
async fn async_increment(state: State<'_, Mutex<AppState>>) -> Result<u32, ()> {
    let mut state = state.lock().await;  // 使用.await而非.unwrap()!
    state.counter += 1;
    Ok(state.counter)
}

// 关键:带借用参数的异步命令需要返回Result类型
// ❌ 无法编译
async fn cmd(state: State<'_, AppState>) { }

// ✅ 正确模式
async fn cmd(state: State<'_, AppState>) -> Result<(), ()> { Ok(()) }
安全:路径遍历防护:
rust
#[tauri::command]
async fn read_file(path: String, app: AppHandle) -> Result<String, String> {
    let path = std::path::Path::new(&path);

    // 防止路径遍历攻击
    if path.components().any(|c| matches!(c, std::path::Component::ParentDir)) {
        return Err("无效路径:不允许目录遍历".into());
    }

    // 验证路径是否在允许的基础目录内
    let base = app.path().app_data_dir().unwrap();
    let full_path = base.join(&path);
    let canonical = full_path.canonicalize()
        .map_err(|e| format!("无效路径: {}", e))?;

    if !canonical.starts_with(&base) {
        return Err("访问被拒绝:路径超出允许范围".into());
    }

    std::fs::read_to_string(canonical).map_err(|e| e.to_string())
}
异步性能(CPU密集型任务使用spawn_blocking):
rust
// CPU密集型工作应使用spawn_blocking
#[tauri::command]
async fn heavy_computation(data: Vec<u8>) -> Result<Vec<u8>, String> {
    tokio::task::spawn_blocking(move || {
        process_heavy_data(data)  // 在阻塞线程池运行
    }).await.map_err(|e| e.to_string())
}

// I/O工作使用常规异步
#[tauri::command]
async fn fetch_data(url: String) -> Result<Data, String> {
    reqwest::get(&url).await
        .map_err(|e| e.to_string())?
        .json().await
        .map_err(|e| e.to_string())
}
AppHandle扩展 trait:
rust
pub trait AppHandleExt {
    fn get_database(&self) -> Arc<Database>;
    fn emit_global(&self, event: &str, payload: impl Serialize);
}

impl AppHandleExt for tauri::AppHandle {
    fn get_database(&self) -> Arc<Database> {
        self.state::<Arc<Database>>().inner().clone()
    }

    fn emit_global(&self, event: &str, payload: impl Serialize) {
        self.emit(event, payload).unwrap();
    }
}

// 命令中的用法:
#[tauri::command]
async fn get_pods(app: AppHandle) -> Result<Vec<Pod>, AppError> {
    let db = app.get_database();
    db.query_pods().await
}
实时更新事件(后端→前端):
rust
use tauri::{AppHandle, Emitter};

#[derive(Clone, Serialize)]
struct ProgressUpdate { percent: u32, status: String }

#[tauri::command]
async fn long_operation(app: AppHandle) -> Result<(), String> {
    for i in 0..=100 {
        app.emit("progress", ProgressUpdate {
            percent: i,
            status: format!("处理进度 {}%", i)
        }).unwrap();
        tokio::time::sleep(Duration::from_millis(50)).await;
    }
    Ok(())
}
typescript
// 前端:务必清理监听器!
import { listen } from '@tauri-apps/api/event';

const unlisten = await listen<ProgressUpdate>('progress', (event) => {
  console.log(`进度: ${event.payload.percent}%`);
});

// 卸载时清理
onCleanup(() => unlisten());
Tauri 2.0基于能力的安全:
json
// src-tauri/capabilities/default.json
{
  "$schema": "../gen/schemas/desktop-schema.json",
  "identifier": "main-capability",
  "windows": ["main"],
  "permissions": [
    "core:default",
    "fs:default",
    {
      "identifier": "fs:allow-read",
      "allow": [{ "path": "$APPDATA/*" }],
      "deny": [{ "path": "$HOME/.ssh/*" }]
    }
  ]
}
发布构建优化:
toml
undefined

Cargo.toml

Cargo.toml

[profile.release] lto = true # Link-time optimization codegen-units = 1 # Better optimization opt-level = "s" # Optimize for size panic = "abort" # Smaller binary strip = true # Remove debug symbols

**Channels for High-Throughput Streaming (Alternative to Events):**

```rust
use tauri::ipc::Channel;

#[derive(Clone, Serialize)]
#[serde(rename_all = "camelCase", tag = "event", content = "data")]
enum DownloadEvent<'a> {
    Started { url: &'a str, size: u64 },
    Progress { percent: u8, downloaded: u64 },
    Finished,
}

#[tauri::command]
fn download(url: String, on_progress: Channel<DownloadEvent>) {
    on_progress.send(DownloadEvent::Started { url: &url, size: 1024 }).unwrap();
    // ... streaming data
    for i in 0..=100 {
        on_progress.send(DownloadEvent::Progress { percent: i, downloaded: i as u64 * 10 }).unwrap();
    }
    on_progress.send(DownloadEvent::Finished).unwrap();
}
typescript
// Frontend: Channel usage
await invoke('download', {
  url: 'https://example.com/file',
  onProgress: new Channel<DownloadEvent>((event) => {
    if (event.event === 'progress') {
      console.log(`Downloaded: ${event.data.percent}%`);
    }
  })
});
Multi-Window Security Isolation:
json
// capabilities/admin.json - More privileges
{
  "identifier": "admin-capability",
  "windows": ["admin-*"],
  "permissions": ["fs:default", "fs:allow-write", "shell:allow-execute"]
}

// capabilities/viewer.json - Read-only
{
  "identifier": "viewer-capability",
  "windows": ["viewer-*"],
  "permissions": ["fs:allow-read"]
}
Content Security Policy (CSP):
json
// tauri.conf.json
{
  "app": {
    "security": {
      "csp": {
        "default-src": "'self' customprotocol: asset:",
        "connect-src": "ipc: http://ipc.localhost",
        "script-src": "'self'",
        "style-src": "'unsafe-inline' 'self'"
      }
    }
  }
}
Security Hardening Checklist:
  • Enable strict CSP with
    default-src 'self'
  • Configure per-window capabilities with minimum permissions
  • Define scopes to restrict file system access
  • Validate ALL command inputs in Rust (frontend is untrusted!)
  • Run
    cargo audit
    and
    npm audit
    regularly
  • Never load remote/untrusted content
  • Sign all release binaries
  • Use
    tokio::sync::Mutex
    for async commands (not std::sync)
Splashscreen Startup Optimization:
rust
tauri::Builder::default()
    .setup(|app| {
        let splashscreen = app.get_webview_window("splashscreen").unwrap();
        let main_window = app.get_webview_window("main").unwrap();

        tauri::async_runtime::spawn(async move {
            // Heavy initialization here (doesn't block UI)
            initialize_database().await;
            load_config().await;

            splashscreen.close().unwrap();
            main_window.show().unwrap();
        });
        Ok(())
    })
Mobile Support (lib.rs Entry Point):
rust
// src-tauri/src/lib.rs
#[cfg_attr(mobile, tauri::mobile_entry_point)]
pub fn run() {
    tauri::Builder::default()
        .invoke_handler(tauri::generate_handler![...])
        .run(tauri::generate_context!())
        .expect("error running app");
}

// src-tauri/src/main.rs (minimal)
fn main() {
    kubeli_lib::run();
}
Testing: Rust Commands with Mock Runtime:
rust
#[cfg(test)]
mod tests {
    use tauri::test::{mock_builder, mock_context, noop_assets};

    fn create_app() -> tauri::App<tauri::test::MockRuntime> {
        mock_builder()
            .invoke_handler(tauri::generate_handler![super::greet])
            .build(mock_context(noop_assets()))
            .expect("failed to build app")
    }

    #[test]
    fn test_greet() {
        let _app = create_app();
        let result = super::greet("World");
        assert_eq!(result, "Hello, World!");
    }
}
toml
undefined
[profile.release] lto = true # 链接时优化 codegen-units = 1 # 更优的优化 opt-level = "s" # 针对大小优化 panic = "abort" # 更小的二进制文件 strip = true # 移除调试符号

**高吞吐量流的通道(事件替代方案):**

```rust
use tauri::ipc::Channel;

#[derive(Clone, Serialize)]
#[serde(rename_all = "camelCase", tag = "event", content = "data")]
enum DownloadEvent<'a> {
    Started { url: &'a str, size: u64 },
    Progress { percent: u8, downloaded: u64 },
    Finished,
}

#[tauri::command]
fn download(url: String, on_progress: Channel<DownloadEvent>) {
    on_progress.send(DownloadEvent::Started { url: &url, size: 1024 }).unwrap();
    // ... 流数据处理
    for i in 0..=100 {
        on_progress.send(DownloadEvent::Progress { percent: i, downloaded: i as u64 * 10 }).unwrap();
    }
    on_progress.send(DownloadEvent::Finished).unwrap();
}
typescript
// 前端:通道用法
await invoke('download', {
  url: 'https://example.com/file',
  onProgress: new Channel<DownloadEvent>((event) => {
    if (event.event === 'progress') {
      console.log(`下载进度: ${event.data.percent}%`);
    }
  })
});
多窗口安全隔离:
json
// capabilities/admin.json - 更高权限
{
  "identifier": "admin-capability",
  "windows": ["admin-*"],
  "permissions": ["fs:default", "fs:allow-write", "shell:allow-execute"]
}

// capabilities/viewer.json - 只读权限
{
  "identifier": "viewer-capability",
  "windows": ["viewer-*"],
  "permissions": ["fs:allow-read"]
}
内容安全策略(CSP):
json
// tauri.conf.json
{
  "app": {
    "security": {
      "csp": {
        "default-src": "'self' customprotocol: asset:",
        "connect-src": "ipc: http://ipc.localhost",
        "script-src": "'self'",
        "style-src": "'unsafe-inline' 'self'"
      }
    }
  }
}
安全强化检查清单:
  • 启用严格CSP,设置
    default-src 'self'
  • 为每个窗口配置最小权限的能力
  • 定义作用域以限制文件系统访问
  • 在Rust中验证所有命令输入(前端不可信!)
  • 定期运行
    cargo audit
    npm audit
  • 绝不加载远程/不可信内容
  • 为所有发布二进制文件签名
  • 异步命令使用
    tokio::sync::Mutex
    (而非std::sync)
启动屏优化:
rust
tauri::Builder::default()
    .setup(|app| {
        let splashscreen = app.get_webview_window("splashscreen").unwrap();
        let main_window = app.get_webview_window("main").unwrap();

        tauri::async_runtime::spawn(async move {
            // 在此处执行重型初始化(不阻塞UI)
            initialize_database().await;
            load_config().await;

            splashscreen.close().unwrap();
            main_window.show().unwrap();
        });
        Ok(())
    })
移动端支持(lib.rs入口):
rust
// src-tauri/src/lib.rs
#[cfg_attr(mobile, tauri::mobile_entry_point)]
pub fn run() {
    tauri::Builder::default()
        .invoke_handler(tauri::generate_handler![...])
        .run(tauri::generate_context!())
        .expect("error running app");
}

// src-tauri/src/main.rs(极简)
fn main() {
    kubeli_lib::run();
}
测试:使用Mock Runtime测试Rust命令:
rust
#[cfg(test)]
mod tests {
    use tauri::test::{mock_builder, mock_context, noop_assets};

    fn create_app() -> tauri::App<tauri::test::MockRuntime> {
        mock_builder()
            .invoke_handler(tauri::generate_handler![super::greet])
            .build(mock_context(noop_assets()))
            .expect("failed to build app")
    }

    #[test]
    fn test_greet() {
        let _app = create_app();
        let result = super::greet("World");
        assert_eq!(result, "Hello, World!");
    }
}
toml
undefined

Enable test feature in Cargo.toml

在Cargo.toml中启用测试特性

[dependencies] tauri = { version = "2.0", features = ["test"] }

**Testing: Frontend IPC Mocking (Vitest):**

```typescript
import { mockIPC, clearMocks } from '@tauri-apps/api/mocks';
import { invoke } from '@tauri-apps/api/core';

afterEach(() => clearMocks());

test('invoke add command', async () => {
  mockIPC((cmd, args) => {
    if (cmd === 'add') return (args as { a: number; b: number }).a + args.b;
  });

  const result = await invoke('add', { a: 12, b: 15 });
  expect(result).toBe(27);
});
Code Quality: Clippy Configuration:
toml
undefined
[dependencies] tauri = { version = "2.0", features = ["test"] }

**测试:前端IPC Mocking(Vitest):**

```typescript
import { mockIPC, clearMocks } from '@tauri-apps/api/mocks';
import { invoke } from '@tauri-apps/api/core';

afterEach(() => clearMocks());

test('invoke add command', async () => {
  mockIPC((cmd, args) => {
    if (cmd === 'add') return (args as { a: number; b: number }).a + args.b;
  });

  const result = await invoke('add', { a: 12, b: 15 });
  expect(result).toBe(27);
});
代码质量:Clippy配置:
toml
undefined

Cargo.toml

Cargo.toml

[lints.clippy] pedantic = { level = "warn", priority = -1 } unwrap_used = "deny" # Force proper error handling expect_used = "warn" module_name_repetitions = "allow"

**Code Quality: rustfmt.toml:**

```toml
edition = "2021"
max_width = 100
imports_granularity = "Module"
group_imports = "StdExternalCrate"
wrap_comments = true
Workspace Dependency Management:
toml
undefined
[lints.clippy] pedantic = { level = "warn", priority = -1 } unwrap_used = "deny" # 强制正确的错误处理 expect_used = "warn" module_name_repetitions = "allow"

**代码质量:rustfmt.toml:**

```toml
edition = "2021"
max_width = 100
imports_granularity = "Module"
group_imports = "StdExternalCrate"
wrap_comments = true
工作区依赖管理:
toml
undefined

Root Cargo.toml

根目录Cargo.toml

[workspace.dependencies] tauri = { version = "2.0", features = [] } serde = { version = "1.0", features = ["derive"] } tokio = { version = "1", features = ["full"] }
[workspace.dependencies] tauri = { version = "2.0", features = [] } serde = { version = "1.0", features = ["derive"] } tokio = { version = "1", features = ["full"] }

Member Cargo.toml - inherit from workspace

子项目Cargo.toml - 继承工作区依赖

[dependencies] tauri.workspace = true serde.workspace = true

---
[dependencies] tauri.workspace = true serde.workspace = true

---

React / TypeScript Patterns

React / TypeScript模式

Component Cohesion (Single Responsibility):
typescript
// BEFORE: Component does too much
function PodManager() {
  const [pods, setPods] = useState([]);
  const [filter, setFilter] = useState('');
  const [sortBy, setSortBy] = useState('name');
  const [selectedPod, setSelectedPod] = useState(null);
  const [isDeleting, setIsDeleting] = useState(false);
  const [showLogs, setShowLogs] = useState(false);
  // ... 200 lines of mixed concerns

  return (
    <div>
      <FilterBar ... />
      <PodList ... />
      <PodDetails ... />
      <DeleteConfirmation ... />
      <LogViewer ... />
    </div>
  );
}

// AFTER: Separated concerns
function PodManager() {
  return (
    <PodFilterProvider>
      <div>
        <PodFilterBar />
        <PodListWithSelection />
        <PodDetailsPanel />
      </div>
    </PodFilterProvider>
  );
}
// Each sub-component manages its own state or uses shared store
Props Interface Simplification:
typescript
// BEFORE: Too many props (shallow module)
interface PodCardProps {
  name: string;
  namespace: string;
  status: string;
  createdAt: Date;
  labels: Record<string, string>;
  onSelect: () => void;
  onDelete: () => void;
  onViewLogs: () => void;
  onRestart: () => void;
  isSelected: boolean;
  showActions: boolean;
}

// AFTER: Deep module with simple interface
interface PodCardProps {
  pod: Pod;
  onAction?: (action: PodAction) => void;
}

type PodAction =
  | { type: 'select' }
  | { type: 'delete' }
  | { type: 'viewLogs' }
  | { type: 'restart' };
Custom Hooks for Reusable Logic:
typescript
// BEFORE: Duplicated logic in components
function PodList() {
  const [data, setData] = useState([]);
  const [loading, setLoading] = useState(false);
  const [error, setError] = useState(null);

  useEffect(() => {
    setLoading(true);
    invoke('get_pods', { namespace })
      .then(setData)
      .catch(setError)
      .finally(() => setLoading(false));
  }, [namespace]);
  // ...
}

// AFTER: Reusable hook
function useTauriQuery<T>(command: string, args: Record<string, unknown>) {
  const [data, setData] = useState<T | null>(null);
  const [loading, setLoading] = useState(false);
  const [error, setError] = useState<Error | null>(null);

  useEffect(() => {
    setLoading(true);
    invoke<T>(command, args)
      .then(setData)
      .catch(setError)
      .finally(() => setLoading(false));
  }, [command, JSON.stringify(args)]);

  return { data, loading, error };
}

// Usage
function PodList({ namespace }: Props) {
  const { data: pods, loading, error } = useTauriQuery<Pod[]>('get_pods', { namespace });
  // ...
}

组件内聚性(单一职责):
typescript
// 重构前:组件职责过多
function PodManager() {
  const [pods, setPods] = useState([]);
  const [filter, setFilter] = useState('');
  const [sortBy, setSortBy] = useState('name');
  const [selectedPod, setSelectedPod] = useState(null);
  const [isDeleting, setIsDeleting] = useState(false);
  const [showLogs, setShowLogs] = useState(false);
  // ... 200多行混合职责代码

  return (
    <div>
      <FilterBar ... />
      <PodList ... />
      <PodDetails ... />
      <DeleteConfirmation ... />
      <LogViewer ... />
    </div>
  );
}

// 重构后:职责分离
function PodManager() {
  return (
    <PodFilterProvider>
      <div>
        <PodFilterBar />
        <PodListWithSelection />
        <PodDetailsPanel />
      </div>
    </PodFilterProvider>
  );
}
// 每个子组件管理自身状态或使用共享Store
Props接口简化:
typescript
// 重构前:Props过多(浅模块)
interface PodCardProps {
  name: string;
  namespace: string;
  status: string;
  createdAt: Date;
  labels: Record<string, string>;
  onSelect: () => void;
  onDelete: () => void;
  onViewLogs: () => void;
  onRestart: () => void;
  isSelected: boolean;
  showActions: boolean;
}

// 重构后:深模块,接口简单
interface PodCardProps {
  pod: Pod;
  onAction?: (action: PodAction) => void;
}

type PodAction =
  | { type: 'select' }
  | { type: 'delete' }
  | { type: 'viewLogs' }
  | { type: 'restart' };
可复用逻辑的自定义Hook:
typescript
// 重构前:组件中重复逻辑
function PodList() {
  const [data, setData] = useState([]);
  const [loading, setLoading] = useState(false);
  const [error, setError] = useState(null);

  useEffect(() => {
    setLoading(true);
    invoke('get_pods', { namespace })
      .then(setData)
      .catch(setError)
      .finally(() => setLoading(false));
  }, [namespace]);
  // ...
}

// 重构后:可复用Hook
function useTauriQuery<T>(command: string, args: Record<string, unknown>) {
  const [data, setData] = useState<T | null>(null);
  const [loading, setLoading] = useState(false);
  const [error, setError] = useState<Error | null>(null);

  useEffect(() => {
    setLoading(true);
    invoke<T>(command, args)
      .then(setData)
      .catch(setError)
      .finally(() => setLoading(false));
  }, [command, JSON.stringify(args)]);

  return { data, loading, error };
}

// 用法
function PodList({ namespace }: Props) {
  const { data: pods, loading, error } = useTauriQuery<Pod[]>('get_pods', { namespace });
  // ...
}

Phase 5: Refactoring Workflow

第五阶段:重构工作流

Step-by-Step Process

分步流程

  1. Analyze (5-10 min)
    • Run
      /software-design-review
      on target code
    • Identify top 3 complexity issues
    • Choose ONE to fix first
  2. Design (5 min)
    • Consider 2-3 alternative approaches
    • Pick the one with simplest interface
    • Write the interface comment FIRST
  3. Test (before coding)
    • Ensure tests exist
    • If not, write characterization tests
    • Run tests to confirm green
  4. Refactor (small steps)
    • Make ONE change at a time
    • Run tests after each change
    • Commit after each working step
  5. Review (after)
    • Does the code look like it was designed this way?
    • Is the interface simpler?
    • Did we improve or just move complexity?
  1. 分析(5-10分钟)
    • 对目标代码运行/software-design-review
    • 确定前3个复杂度问题
    • 选择一个优先修复
  2. 设计(5分钟)
    • 考虑2-3种替代方案
    • 选择接口最简单的方案
    • 先编写接口注释
  3. 测试(编码前)
    • 确保测试存在
    • 若不存在,编写特征测试
    • 运行测试确认全部通过
  4. 重构(小步推进)
    • 每次只做一处修改
    • 每次修改后运行测试
    • 每次工作步骤完成后提交代码
  5. 评审(完成后)
    • 代码是否看起来从一开始就是这样设计的?
    • 接口是否更简单?
    • 我们是降低了复杂度还是只是转移了复杂度?

Commit Strategy

提交策略

bash
undefined
bash
undefined

Small, atomic commits

小而独立的提交

git commit -m "refactor(pods): extract PodCard props into Pod type" git commit -m "refactor(pods): create usePod hook for selective access" git commit -m "refactor(pods): move pod filtering to dedicated hook"
git commit -m "refactor(pods): extract PodCard props into Pod type" git commit -m "refactor(pods): create usePod hook for selective access" git commit -m "refactor(pods): move pod filtering to dedicated hook"

NOT one giant commit

不要提交大而全的代码

git commit -m "refactor: improve pod management" # BAD: too vague

---
git commit -m "refactor: improve pod management" # 不良:过于模糊

---

Phase 6: Prioritization Matrix

第六阶段:优先级矩阵

Rate each issue and fix highest impact first:
IssueComplexity ReductionEffortRiskPriority
High impact, Low effort, Low risk⬆️⬆️⬆️⬇️⬇️P0 - Do First
High impact, Medium effort⬆️⬆️⬆️➡️➡️P1
Medium impact, Low effort⬆️⬆️⬇️⬇️P2
Low impact OR High risk⬆️Any⬆️P3 - Later

对每个问题评分,优先修复影响最高的问题:
问题复杂度降低程度工作量风险优先级
高影响、低工作量、低风险⬆️⬆️⬆️⬇️⬇️P0 - 立即处理
高影响、中等工作量⬆️⬆️⬆️➡️➡️P1
中等影响、低工作量⬆️⬆️⬇️⬇️P2
低影响或高风险⬆️任意⬆️P3 - 后续处理

Your Output Format

输出格式

1. Analysis Summary

1. 分析摘要

text
Target: [file/directory]
Current Complexity: [Low/Medium/High]
Top Issues:
1. [Issue + Principle violated]
2. [Issue + Principle violated]
3. [Issue + Principle violated]
text
目标: [文件/目录]
当前复杂度: [低/中/高]
主要问题:
1. [问题 + 违反的原则]
2. [问题 + 违反的原则]
3. [问题 + 违反的原则]

2. Refactoring Plan

2. 重构计划

text
Priority | Issue | Refactoring | Estimated Changes
---------|-------|-------------|------------------
P0       | ...   | ...         | ~X files, ~Y lines
P1       | ...   | ...         | ...
text
优先级 | 问题 | 重构方案 | 预计变更范围
---------|-------|-------------|------------------
P0       | ...   | ...         | ~X个文件,~Y行代码
P1       | ...   | ...         | ...

3. Step-by-Step Execution

3. 分步执行

For each P0/P1 item:
  1. What to change
  2. Expected interface (comment first)
  3. Test requirements
  4. Implementation steps
针对每个P0/P1项:
  1. 要修改的内容
  2. 预期接口(先写注释)
  3. 测试要求
  4. 实现步骤

4. Safety Notes

4. 安全注意事项

  • Tests to add/verify
  • Potential breaking changes
  • Rollback plan if needed

  • 需要添加/验证的测试
  • 潜在的破坏性变更
  • 回滚方案(若需要)

Sources & References

参考资料

Books

书籍

  • John Ousterhout: "A Philosophy of Software Design" (15 Principles)
  • Robert C. Martin: "Clean Code" (Smells & Heuristics)
  • Martin Fowler: "Refactoring" (Refactoring Catalog)
  • John Ousterhout: 《软件设计哲学》(15条原则)
  • Robert C. Martin: 《整洁代码》(坏味道与启发式规则)
  • Martin Fowler: 《重构》(重构目录)

Web Resources

在线资源