程序员武学修炼手册(二):进阶篇——小有所成,从能跑就行到知其所以然

"入门弟子求的是'能用',三流高手求的是'好用'。" ------ 《程序员武学心法》

前情回顾

在上一篇中,我们讲述了初学乍练的修炼------那个"Hello World"到"能跑就行"的阶段。

当你开始思考"为什么要这样写"而不是"怎么让它跑起来"的时候,恭喜你,你已经踏入了小有所成的大门,成为了江湖上的三流高手。


第一章:小有所成的特征

1.1 什么是小有所成?

小有所成,是程序员从"会写代码"到"会写好代码"的关键阶段。

就像武侠小说里,弟子从"会几招花拳绣腿"到"开始修炼内功心法"的转变,虽然还算不上一流高手,但已经能在江湖上行走了。

小有所成程序员的典型特征:

  • 开始关注代码规范
  • 知道什么是"代码坏味道"
  • 会写单元测试(虽然经常偷懒不写)
  • 开始使用设计模式(虽然经常用错)
  • Git 不再只会addcommitpush

1.2 初学乍练 vs 小有所成

javascript 复制代码
// 同一个需求,不同境界的实现

// 需求:根据用户类型返回不同的折扣

// 初学乍练写法
function getDiscount(userType) {
  if (userType === "normal") {
    return 0
  } else if (userType === "vip") {
    return 0.1
  } else if (userType === "svip") {
    return 0.2
  } else if (userType === "partner") {
    return 0.3
  } else {
    return 0
  }
}

// 小有所成写法
const DISCOUNT_MAP = {
  normal: 0,
  vip: 0.1,
  svip: 0.2,
  partner: 0.3,
}

function getDiscount(userType) {
  return DISCOUNT_MAP[userType] ?? 0
}

// 区别:
// 1. 配置与逻辑分离
// 2. 新增类型只需改配置,不需改代码
// 3. 更容易测试
// 4. 更容易理解

第二章:小有所成的修炼内容

2.1 第一式:代码规范

javascript 复制代码
// 小有所成必修:代码规范

// ===== 命名规范 =====

// 变量:小驼峰,名词
const userName = "张三"
const userAge = 18
const isActive = true // 布尔值用is/has/can开头

// 函数:小驼峰,动词开头
function getUserInfo() {}
function calculateTotal() {}
function validateEmail() {}

// 类:大驼峰
class UserService {}
class OrderManager {}

// 常量:全大写,下划线分隔
const MAX_RETRY_COUNT = 3
const API_BASE_URL = "https://api.example.com"

// ===== 格式规范 =====

// 缩进:2空格或4空格,团队统一即可
// 行宽:80-120字符
// 空行:逻辑块之间空一行

function processOrder(order) {
  // 参数校验
  if (!order) {
    throw new Error("Order is required")
  }

  // 业务逻辑
  const total = calculateTotal(order)
  const discount = getDiscount(order.user)
  const finalPrice = total * (1 - discount)

  // 返回结果
  return {
    orderId: order.id,
    finalPrice,
  }
}

2.2 第二式:函数的艺术

javascript 复制代码
// 小有所成的函数修炼

// ===== 原则1:单一职责 =====

// 不好:一个函数做太多事
function processUser(user) {
  // 验证
  if (!user.email) throw new Error("Email required")
  if (!user.name) throw new Error("Name required")

  // 格式化
  user.email = user.email.toLowerCase()
  user.name = user.name.trim()

  // 保存
  database.save(user)

  // 发送邮件
  sendWelcomeEmail(user.email)

  // 记录日志
  logger.info("User created", user)
}

// 好:每个函数只做一件事
function validateUser(user) {
  if (!user.email) throw new Error("Email required")
  if (!user.name) throw new Error("Name required")
}

function formatUser(user) {
  return {
    ...user,
    email: user.email.toLowerCase(),
    name: user.name.trim(),
  }
}

function createUser(user) {
  validateUser(user)
  const formattedUser = formatUser(user)
  database.save(formattedUser)
  sendWelcomeEmail(formattedUser.email)
  logger.info("User created", formattedUser)
}

// ===== 原则2:参数不要太多 =====

// 不好:参数太多
function createOrder(
  userId,
  productId,
  quantity,
  price,
  discount,
  address,
  phone,
  note
) {
  // ...
}

// 好:使用对象参数
function createOrder(options) {
  const { userId, productId, quantity, price, discount, address, phone, note } =
    options
  // ...
}

// 或者使用TypeScript定义接口
interface CreateOrderOptions {
  userId: string;
  productId: string;
  quantity: number;
  price: number;
  discount?: number;
  address: string;
  phone: string;
  note?: string;
}

function createOrder(options: CreateOrderOptions) {
  // ...
}

// ===== 原则3:避免副作用 =====

// 不好:修改了传入的参数
function addItem(cart, item) {
  cart.items.push(item) // 修改了原数组
  cart.total += item.price // 修改了原对象
  return cart
}

// 好:返回新对象
function addItem(cart, item) {
  return {
    ...cart,
    items: [...cart.items, item],
    total: cart.total + item.price,
  }
}

2.3 第三式:错误处理

javascript 复制代码
// 小有所成的错误处理修炼

// ===== 初学乍练的错误处理 =====
function getUser(id) {
  try {
    return database.findUser(id)
  } catch (e) {
    console.log(e) // 打印一下就完事了
    return null
  }
}

// ===== 小有所成的错误处理 =====

// 1. 定义明确的错误类型
class ValidationError extends Error {
  constructor(message) {
    super(message)
    this.name = "ValidationError"
  }
}

class NotFoundError extends Error {
  constructor(resource, id) {
    super(`${resource} with id ${id} not found`)
    this.name = "NotFoundError"
    this.resource = resource
    this.id = id
  }
}

class DatabaseError extends Error {
  constructor(message, originalError) {
    super(message)
    this.name = "DatabaseError"
    this.originalError = originalError
  }
}

// 2. 在合适的层级处理错误
function getUser(id) {
  if (!id) {
    throw new ValidationError("User ID is required")
  }

  try {
    const user = database.findUser(id)
    if (!user) {
      throw new NotFoundError("User", id)
    }
    return user
  } catch (error) {
    if (error instanceof NotFoundError) {
      throw error // 业务错误,继续抛出
    }
    // 数据库错误,包装后抛出
    throw new DatabaseError("Failed to fetch user", error)
  }
}

// 3. 在最外层统一处理
async function handleRequest(req, res) {
  try {
    const user = await getUser(req.params.id)
    res.json(user)
  } catch (error) {
    if (error instanceof ValidationError) {
      res.status(400).json({ error: error.message })
    } else if (error instanceof NotFoundError) {
      res.status(404).json({ error: error.message })
    } else {
      // 未知错误,记录日志,返回通用错误
      logger.error("Unexpected error", error)
      res.status(500).json({ error: "Internal server error" })
    }
  }
}

2.4 第四式:单元测试

javascript 复制代码
// 小有所成必修:单元测试

// 被测试的函数
function calculatePrice(basePrice, quantity, discount = 0) {
  if (basePrice < 0 || quantity < 0) {
    throw new Error("Price and quantity must be non-negative")
  }
  if (discount < 0 || discount > 1) {
    throw new Error("Discount must be between 0 and 1")
  }

  const subtotal = basePrice * quantity
  const discountAmount = subtotal * discount
  return subtotal - discountAmount
}

// 单元测试
describe("calculatePrice", () => {
  // 正常情况
  test("should calculate price without discount", () => {
    expect(calculatePrice(100, 2)).toBe(200)
  })

  test("should calculate price with discount", () => {
    expect(calculatePrice(100, 2, 0.1)).toBe(180)
  })

  test("should handle zero quantity", () => {
    expect(calculatePrice(100, 0)).toBe(0)
  })

  // 边界情况
  test("should handle 100% discount", () => {
    expect(calculatePrice(100, 2, 1)).toBe(0)
  })

  // 错误情况
  test("should throw error for negative price", () => {
    expect(() => calculatePrice(-100, 2)).toThrow(
      "Price and quantity must be non-negative"
    )
  })

  test("should throw error for invalid discount", () => {
    expect(() => calculatePrice(100, 2, 1.5)).toThrow(
      "Discount must be between 0 and 1"
    )
  })
})

// 测试覆盖的三个维度:
// 1. 正常路径(Happy Path)
// 2. 边界条件(Edge Cases)
// 3. 错误情况(Error Cases)

2.5 第五式:设计模式入门

javascript 复制代码
// 小有所成的设计模式修炼

// ===== 模式1:单例模式 =====
// 场景:全局只需要一个实例

class Logger {
  static instance = null

  static getInstance() {
    if (!Logger.instance) {
      Logger.instance = new Logger()
    }
    return Logger.instance
  }

  log(message) {
    console.log(`[${new Date().toISOString()}] ${message}`)
  }
}

// 使用
const logger1 = Logger.getInstance()
const logger2 = Logger.getInstance()
console.log(logger1 === logger2) // true

// ===== 模式2:工厂模式 =====
// 场景:根据条件创建不同类型的对象

class UserFactory {
  static create(type, data) {
    switch (type) {
      case "admin":
        return new AdminUser(data)
      case "vip":
        return new VipUser(data)
      default:
        return new NormalUser(data)
    }
  }
}

// 使用
const admin = UserFactory.create("admin", { name: "管理员" })
const vip = UserFactory.create("vip", { name: "VIP用户" })

// ===== 模式3:策略模式 =====
// 场景:根据不同情况使用不同的算法

// 不好:if-else地狱
function calculateShipping(type, weight) {
  if (type === "standard") {
    return weight * 5
  } else if (type === "express") {
    return weight * 10
  } else if (type === "overnight") {
    return weight * 20
  }
}

// 好:策略模式
const shippingStrategies = {
  standard: (weight) => weight * 5,
  express: (weight) => weight * 10,
  overnight: (weight) => weight * 20,
}

function calculateShipping(type, weight) {
  const strategy = shippingStrategies[type]
  if (!strategy) {
    throw new Error(`Unknown shipping type: ${type}`)
  }
  return strategy(weight)
}

// ===== 模式4:观察者模式 =====
// 场景:一个对象状态变化时通知其他对象

class EventEmitter {
  constructor() {
    this.listeners = {}
  }

  on(event, callback) {
    if (!this.listeners[event]) {
      this.listeners[event] = []
    }
    this.listeners[event].push(callback)
  }

  off(event, callback) {
    if (!this.listeners[event]) return
    this.listeners[event] = this.listeners[event].filter(
      (cb) => cb !== callback
    )
  }

  emit(event, data) {
    if (!this.listeners[event]) return
    this.listeners[event].forEach((callback) => callback(data))
  }
}

// 使用
const emitter = new EventEmitter()

emitter.on("userCreated", (user) => {
  console.log("发送欢迎邮件给", user.email)
})

emitter.on("userCreated", (user) => {
  console.log("记录日志", user.name)
})

emitter.emit("userCreated", { name: "张三", email: "test@example.com" })

2.6 第六式:Git 进阶

bash 复制代码
# 筑基期的Git修炼

# ===== 练气期的Git =====
git add .
git commit -m "update"
git push
# 完事!

# ===== 小有所成的Git =====

# 1. 有意义的提交信息
git commit -m "feat: 添加用户注册功能"
git commit -m "fix: 修复登录页面样式错位问题"
git commit -m "refactor: 重构订单计算逻辑"
git commit -m "docs: 更新API文档"
git commit -m "test: 添加用户服务单元测试"

# 提交信息格式:<type>: <description>
# type: feat, fix, refactor, docs, test, chore, style

# 2. 分支管理
git checkout -b feature/user-registration  # 功能分支
git checkout -b bugfix/login-style         # 修复分支
git checkout -b hotfix/security-patch      # 紧急修复

# 3. 合并前先rebase
git checkout feature/user-registration
git rebase main  # 把main的最新代码合并进来
git checkout main
git merge feature/user-registration

# 4. 交互式rebase整理提交
git rebase -i HEAD~3  # 整理最近3个提交
# 可以合并、修改、删除提交

# 5. 暂存工作区
git stash           # 暂存当前修改
git stash list      # 查看暂存列表
git stash pop       # 恢复最近的暂存
git stash drop      # 删除最近的暂存

# 6. 查看历史
git log --oneline --graph  # 图形化查看提交历史
git blame file.js          # 查看每行代码是谁写的
git diff HEAD~1            # 查看最近一次提交的改动

第三章:小有所成的常见瓶颈

3.1 过度设计

javascript 复制代码
// 症状:简单问题复杂化

// 需求:计算两个数的和

// 过度设计版本
class Calculator {
  constructor(strategy) {
    this.strategy = strategy
  }

  setStrategy(strategy) {
    this.strategy = strategy
  }

  execute(a, b) {
    return this.strategy.calculate(a, b)
  }
}

class AddStrategy {
  calculate(a, b) {
    return a + b
  }
}

class CalculatorFactory {
  static create(type) {
    switch (type) {
      case "add":
        return new Calculator(new AddStrategy())
      // ...更多策略
    }
  }
}

// 使用
const calculator = CalculatorFactory.create("add")
const result = calculator.execute(1, 2)

// 正常版本
function add(a, b) {
  return a + b
}

const result = add(1, 2)

// 教训:不要为了用设计模式而用设计模式
// YAGNI原则:You Ain't Gonna Need It

3.2 测试覆盖率焦虑

javascript 复制代码
// 症状:追求100%测试覆盖率

// 被测试的代码
function greet(name) {
  return `Hello, ${name}!`
}

// 过度测试
describe("greet", () => {
  test("should greet with name", () => {
    expect(greet("World")).toBe("Hello, World!")
  })

  test("should greet with different name", () => {
    expect(greet("Alice")).toBe("Hello, Alice!")
  })

  test("should greet with another name", () => {
    expect(greet("Bob")).toBe("Hello, Bob!")
  })

  test("should greet with Chinese name", () => {
    expect(greet("张三")).toBe("Hello, 张三!")
  })

  // 这些测试本质上是一样的,没有额外价值
})

// 合理的测试
describe("greet", () => {
  test("should return greeting with provided name", () => {
    expect(greet("World")).toBe("Hello, World!")
  })

  test("should handle empty name", () => {
    expect(greet("")).toBe("Hello, !")
  })

  // 测试有意义的边界情况,而不是重复测试同样的逻辑
})

3.3 规范教条主义

javascript 复制代码
// 症状:死守规范,不知变通

// 规范说:函数不超过20行
// 于是...

function processOrder_part1(order) {
  // 第1-20行
}

function processOrder_part2(order) {
  // 第21-40行
}

function processOrder_part3(order) {
  // 第41-60行
}

function processOrder(order) {
  processOrder_part1(order)
  processOrder_part2(order)
  processOrder_part3(order)
}

// 这样拆分毫无意义,反而更难理解

// 正确理解:规范是指导,不是法律
// 如果一个函数逻辑上是一个整体,50行也可以接受
// 关键是:代码是否清晰、是否容易理解

第四章:小有所成的突破契机

4.1 第一次重构遗留代码

javascript 复制代码
// 你接手的代码
function process(d) {
  var r = []
  for (var i = 0; i < d.length; i++) {
    if (d[i].t == 1) {
      if (d[i].s == "a") {
        r.push({ n: d[i].n, v: d[i].v * 1.1 })
      } else {
        r.push({ n: d[i].n, v: d[i].v })
      }
    } else if (d[i].t == 2) {
      if (d[i].s == "a") {
        r.push({ n: d[i].n, v: d[i].v * 1.2 })
      } else {
        r.push({ n: d[i].n, v: d[i].v * 1.05 })
      }
    }
  }
  return r
}

// 你的重构
const MULTIPLIERS = {
  1: { a: 1.1, default: 1 },
  2: { a: 1.2, default: 1.05 },
}

function calculateValue(item) {
  const typeMultipliers = MULTIPLIERS[item.type]
  if (!typeMultipliers) return item.value

  const multiplier = typeMultipliers[item.status] || typeMultipliers.default
  return item.value * multiplier
}

function processItems(items) {
  return items
    .filter((item) => MULTIPLIERS[item.type])
    .map((item) => ({
      name: item.name,
      value: calculateValue(item),
    }))
}

// 重构的收获:
// 1. 理解了代码的业务逻辑
// 2. 学会了如何让代码更清晰
// 3. 体会到了好代码和烂代码的区别

4.2 第一次 Code Review 别人

javascript 复制代码
// 你Review的代码
function getUserData(userId) {
  return fetch("/api/users/" + userId)
    .then((res) => res.json())
    .then((data) => {
      return data
    })
    .catch((err) => {
      console.log(err)
    })
}

// 你的Review意见
/*
 * 1. 使用模板字符串代替字符串拼接
 * 2. .then(data => { return data }) 可以简化
 * 3. 错误处理不完整,catch后应该重新抛出或返回默认值
 * 4. 建议使用async/await提高可读性
 *
 * 建议修改为:
 */
async function getUserData(userId) {
  try {
    const response = await fetch(`/api/users/${userId}`)
    if (!response.ok) {
      throw new Error(`HTTP error! status: ${response.status}`)
    }
    return await response.json()
  } catch (error) {
    console.error("Failed to fetch user:", error)
    throw error // 让调用者决定如何处理
  }
}

// Review别人代码的收获:
// 1. 巩固了自己的知识
// 2. 学会了从不同角度看问题
// 3. 提高了代码审美

4.3 第一次性能优化

javascript 复制代码
// 问题:页面加载很慢

// 原始代码
function renderUserList(users) {
  const container = document.getElementById("user-list")

  users.forEach((user) => {
    const div = document.createElement("div")
    div.innerHTML = `
      <h3>${user.name}</h3>
      <p>${user.email}</p>
    `
    container.appendChild(div) // 每次都触发重排
  })
}

// 优化后
function renderUserList(users) {
  const container = document.getElementById("user-list")
  const fragment = document.createDocumentFragment()

  users.forEach((user) => {
    const div = document.createElement("div")
    div.innerHTML = `
      <h3>${user.name}</h3>
      <p>${user.email}</p>
    `
    fragment.appendChild(div) // 先添加到fragment
  })

  container.appendChild(fragment) // 一次性添加到DOM
}

// 进一步优化:虚拟列表
function renderUserListVirtual(users, visibleCount = 20) {
  // 只渲染可见区域的元素
  // 滚动时动态更新
  // ...
}

// 性能优化的收获:
// 1. 理解了浏览器渲染原理
// 2. 学会了性能分析工具
// 3. 知道了"过早优化是万恶之源",但也知道了什么时候该优化

第五章:小有所成的修炼心法

5.1 心法一:代码是负债,不是资产

javascript 复制代码
// 初学乍练的认知
// "我写了1000行代码,好有成就感!"

// 筑基期的认知
// "代码越少越好,能用100行解决的问题,不要写200行"

// 实践
// 不好:代码很多,但很多是重复的
function validateName(name) {
  if (!name) return false
  if (name.length < 2) return false
  if (name.length > 50) return false
  return true
}

function validateEmail(email) {
  if (!email) return false
  if (!email.includes("@")) return false
  if (email.length > 100) return false
  return true
}

function validatePhone(phone) {
  if (!phone) return false
  if (phone.length !== 11) return false
  if (!/^\d+$/.test(phone)) return false
  return true
}

// 好:抽象公共逻辑
const validators = {
  name: [
    { check: (v) => !!v, message: "Name is required" },
    { check: (v) => v.length >= 2, message: "Name too short" },
    { check: (v) => v.length <= 50, message: "Name too long" },
  ],
  email: [
    { check: (v) => !!v, message: "Email is required" },
    { check: (v) => v.includes("@"), message: "Invalid email" },
    { check: (v) => v.length <= 100, message: "Email too long" },
  ],
  phone: [
    { check: (v) => !!v, message: "Phone is required" },
    { check: (v) => v.length === 11, message: "Phone must be 11 digits" },
    { check: (v) => /^\d+$/.test(v), message: "Phone must be numeric" },
  ],
}

function validate(field, value) {
  const rules = validators[field]
  for (const rule of rules) {
    if (!rule.check(value)) {
      return { valid: false, message: rule.message }
    }
  }
  return { valid: true }
}

5.2 心法二:可读性优于性能(大多数时候)

javascript 复制代码
// 初学乍练的认知
// "这样写性能更好!"

// 筑基期的认知
// "先让代码可读,性能问题等出现了再优化"

// 例子:找出数组中的最大值

// "高性能"版本
function findMax(arr) {
  let max = arr[0]
  for (let i = 1, len = arr.length; i < len; i++) {
    if (arr[i] > max) max = arr[i]
  }
  return max
}

// 可读版本
function findMax(arr) {
  return Math.max(...arr)
}

// 除非你处理的是百万级数据,否则可读性更重要
// 过早优化是万恶之源 ------ Donald Knuth

5.3 心法三:写代码前先想清楚

javascript 复制代码
// 初学乍练的做法
// 想到哪写到哪,边写边改

// 筑基期的做法
// 先设计,再编码

// 设计步骤:
// 1. 明确需求:这个功能要做什么?
// 2. 定义接口:输入是什么?输出是什么?
// 3. 考虑边界:有哪些特殊情况?
// 4. 拆分任务:可以分成哪几个步骤?
// 5. 开始编码

// 例子:实现一个分页函数

// 1. 明确需求
// 输入:数组、页码、每页数量
// 输出:当前页的数据、总页数、是否有上/下一页

// 2. 定义接口
interface PaginationResult<T> {
  data: T[];
  currentPage: number;
  totalPages: number;
  hasNextPage: boolean;
  hasPrevPage: boolean;
}

function paginate<T>(
  items: T[],
  page: number,
  pageSize: number
): PaginationResult<T>;

// 3. 考虑边界
// - 空数组
// - 页码小于1
// - 页码大于总页数
// - pageSize为0或负数

// 4. 拆分任务
// - 参数校验
// - 计算总页数
// - 截取当前页数据
// - 组装返回结果

// 5. 开始编码
function paginate<T>(
  items: T[],
  page: number = 1,
  pageSize: number = 10
): PaginationResult<T> {
  // 参数校验
  if (pageSize <= 0) pageSize = 10;
  if (page < 1) page = 1;

  // 计算总页数
  const totalPages = Math.ceil(items.length / pageSize);

  // 修正页码
  if (page > totalPages && totalPages > 0) page = totalPages;

  // 截取当前页数据
  const start = (page - 1) * pageSize;
  const data = items.slice(start, start + pageSize);

  // 组装返回结果
  return {
    data,
    currentPage: page,
    totalPages,
    hasNextPage: page < totalPages,
    hasPrevPage: page > 1,
  };
}

第六章:小有所成的出师考核

6.1 自测题

javascript 复制代码
// 问题1:重构这段代码
function calc(t, a) {
  if (t == "add") {
    return a[0] + a[1]
  } else if (t == "sub") {
    return a[0] - a[1]
  } else if (t == "mul") {
    return a[0] * a[1]
  } else if (t == "div") {
    if (a[1] == 0) {
      return "error"
    }
    return a[0] / a[1]
  }
}

// 问题2:这段代码有什么问题?如何改进?
async function getUsers() {
  const users = await fetch("/api/users").then((r) => r.json())
  const result = []
  for (const user of users) {
    const orders = await fetch(`/api/orders?userId=${user.id}`).then((r) =>
      r.json()
    )
    result.push({ ...user, orders })
  }
  return result
}

// 问题3:为这个函数写单元测试
function formatDate(date, format = "YYYY-MM-DD") {
  const d = new Date(date)
  if (isNaN(d.getTime())) {
    throw new Error("Invalid date")
  }

  const year = d.getFullYear()
  const month = String(d.getMonth() + 1).padStart(2, "0")
  const day = String(d.getDate()).padStart(2, "0")

  return format.replace("YYYY", year).replace("MM", month).replace("DD", day)
}

6.2 出师标准

css 复制代码
┌─────────────────────────────────────────────────────────────┐
│              小有所成出师标准 ✓                              │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│   □ 代码符合团队规范,通过Code Review                        │
│   □ 能写出清晰、可维护的代码                                 │
│   □ 会写单元测试,理解测试的价值                             │
│   □ 熟练使用Git进行版本控制                                  │
│   □ 了解常用设计模式,知道什么时候该用                       │
│   □ 能独立完成中等复杂度的功能开发                           │
│   □ 能重构遗留代码,提高代码质量                             │
│   □ 开始关注性能,但不过早优化                               │
│                                                             │
└─────────────────────────────────────────────────────────────┘

结语:小有所成的意义

小有所成是程序员从"会写代码"到"会写好代码"的关键阶段。

在这个阶段,你会:

  • 开始关注代码质量,而不只是"能跑"
  • 学会用规范和模式来组织代码
  • 理解测试的价值
  • 开始有代码审美

小有所成的核心是:建立正确的编程习惯和思维方式。

这些习惯和思维方式,将成为你未来成长的基础。

就像武侠小说里,内功心法比招式更重要。一个内功深厚的人,学什么招式都快;一个只会花拳绣腿的人,永远只能停留在表面。

下一篇,我们将进入融会贯通------当你开始思考系统架构、开始能够独当一面的时候,你就踏入了一流高手的大门。


预告:融会贯通

在融会贯通阶段,你将学习:

  • 系统设计与架构
  • 技术选型与权衡
  • 团队协作与沟通
  • 项目管理基础
  • 如何成为技术骨干

敬请期待!


本文是《程序员武学修炼手册》系列的第二篇。

如果你正处于小有所成阶段,恭喜你已经超越了大多数"能跑就行"的程序员。

继续修炼,一流高手在向你招手! 💪

相关推荐
小画家~16 小时前
第四十六: channel 高级使用
java·前端·数据库
小贵子的博客16 小时前
Ant Design Vue <a-table>
前端·javascript·vue.js·anti-design-vue
m0_5027249516 小时前
vue动态设置背景图片后显示异常
前端·css
console.log('npc')16 小时前
vue2中子组件父组件的修改参数
开发语言·前端·javascript
奋斗吧程序媛17 小时前
vue3 Study(1)
前端·javascript·vue.js
@Autowire17 小时前
Layout-position
前端·css
QQ129584550417 小时前
ThingsBoard - APP首页修改为手工选择组织
前端·javascript·物联网·iot
whyfail17 小时前
前端数据存储新选择:IndexedDB与Dexie.js技术指南
前端·javascript·数据库
椰果uu17 小时前
vue-virtual-scroller-虚拟滚动列表:渲染不定高度长列表+可控跳转
前端·javascript·typescript·vue