"如果它看起来很蠢,但它能用,那它就不蠢。" ------ 某位凌晨三点还在改 Bug 的程序员
前言:什么是"邪修"?
在程序员的世界里,有两种解决方案:
正道 :设计模式、最佳实践、代码规范、架构优雅 邪修:能跑就行、先上再说、TODO 永远不改、这个 Bug 是 Feature
每个程序员都知道应该走正道,但每个程序员也都有过这样的时刻:
- 产品经理说"明天上线"
- 老板说"这个很简单吧"
- 测试说"这个 Bug 必须今天修"
- 客户说"我们付了钱的"
于是,邪修就成了生存必备技能。
免责声明:本文技巧仅供紧急情况使用。如果你的代码审查者看到这些,请假装不认识我。
第一章:前端邪修大全
1.1 CSS 的黑魔法
问题:这个元素死活对不齐
css
/* 正道:用Flexbox或Grid仔细布局 */
.container {
display: flex;
justify-content: center;
align-items: center;
}
/* 邪修:暴力解决 */
.element {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
/* 如果还不行,加这个 */
margin-top: -3px; /* 玄学数字,别问为什么 */
}
问题:z-index 不生效
css
/* 正道:理解层叠上下文,合理规划z-index */
/* 邪修:数字大力出奇迹 */
.modal {
z-index: 99999;
}
.modal-overlay {
z-index: 99998;
}
.dropdown {
z-index: 99997;
}
/* 终极邪修:当99999都不够用时 */
.super-important-element {
z-index: 2147483647; /* 32位整数最大值,不服来战 */
}
问题:样式被覆盖了
css
/* 正道:检查选择器优先级,重构CSS结构 */
/* 邪修:!important大法 */
.my-button {
background-color: red !important;
color: white !important;
/* 如果还不行 */
background-color: red !important !important; /* 开玩笑的,这不行 */
}
/* 终极邪修:内联样式 + !important */
html
<div style="color: red !important;">我说红就是红</div>
1.2 JavaScript 的野路子
问题:异步回调地狱
javascript
// 正道:使用async/await,优雅处理异步
// 邪修:setTimeout解决一切时序问题
function hackyFix() {
doSomething()
setTimeout(() => {
// 等一下,让上面的先执行完
doSomethingElse()
}, 0)
setTimeout(() => {
// 再等一下
doAnotherThing()
}, 100)
setTimeout(() => {
// 保险起见,多等一会
finalThing()
}, 500)
}
// 更邪的版本:当你不知道要等多久
function reallyHackyFix() {
const checkInterval = setInterval(() => {
if (window.someGlobalVariable) {
clearInterval(checkInterval)
doTheThing()
}
}, 100)
// 保险起见,10秒后强制执行
setTimeout(() => {
clearInterval(checkInterval)
doTheThing() // 管它有没有准备好
}, 10000)
}
问题:数据类型转换
javascript
// 正道:使用明确的类型转换方法
const num = parseInt(str, 10)
const str = String(num)
const bool = Boolean(value)
// 邪修:JavaScript的隐式转换黑魔法
const num = +str // 字符串转数字
const str = "" + num // 数字转字符串
const bool = !!value // 任意值转布尔
const int = ~~floatNum // 浮点转整数(比Math.floor快)
const arr = [...str] // 字符串转数组
// 更骚的操作
const isNumber = value === +value // 检查是否为数字
const isEmpty = !value?.length // 检查是否为空
const defaultVal = value || "default" // 默认值(小心0和'')
const safeVal = value ?? "default" // 更安全的默认值
问题:深拷贝对象
javascript
// 正道:使用structuredClone或lodash.cloneDeep
const copy = structuredClone(original)
const copy = _.cloneDeep(original)
// 邪修:JSON大法
const copy = JSON.parse(JSON.stringify(original))
// 注意:会丢失函数、undefined、Symbol、循环引用会报错
// 更邪的:当你只需要浅拷贝
const copy = { ...original }
const arrCopy = [...originalArr]
// 终极邪修:当你需要"差不多"的拷贝
const almostCopy = Object.assign({}, original)
1.3 React 的骚操作
jsx
// 问题:组件不更新
// 正道:检查state和props的变化,使用正确的更新方式
// 邪修:强制更新
class HackyComponent extends React.Component {
forceUpdateHack = () => {
this.forceUpdate() // React官方不推荐,但它就是能用
}
}
// 函数组件的邪修版本
function HackyFunctionalComponent() {
const [, forceUpdate] = useState(0)
const hackyForceUpdate = () => {
forceUpdate((n) => n + 1) // 改变state强制重渲染
}
return <div>...</div>
}
// 更邪的:key大法
function ParentComponent() {
const [resetKey, setResetKey] = useState(0)
const resetChild = () => {
setResetKey((k) => k + 1) // 改变key,子组件完全重新挂载
}
return <ChildComponent key={resetKey} />
}
jsx
// 问题:useEffect依赖项警告烦死了
// 正道:正确处理依赖项
// 邪修:禁用警告
useEffect(() => {
doSomething(someValue)
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []) // 我就是要空依赖,你管我
// 更邪的:用ref绕过依赖
function HackyComponent({ callback }) {
const callbackRef = useRef(callback)
callbackRef.current = callback // 每次渲染更新ref
useEffect(() => {
// 使用ref,不需要把callback加入依赖
callbackRef.current()
}, []) // 真正的空依赖,不报警告
}
第二章:后端邪修大全
2.1 数据库的野路子
问题:查询太慢了
sql
-- 正道:分析执行计划,添加合适的索引,优化查询结构
-- 邪修:加索引大法
CREATE INDEX idx_everything ON users(name, email, phone, address, created_at);
-- 索引加多了?不存在的,磁盘空间不要钱
-- 更邪的:查询提示强制使用索引
SELECT /*+ INDEX(users idx_name) */ * FROM users WHERE name = 'test';
-- 终极邪修:直接缓存结果
-- 在代码里
const result = cache.get('slow_query_result') || await db.query(slowQuery);
cache.set('slow_query_result', result, 3600); // 缓存1小时,管它数据准不准
问题:N+1 查询
python
# 正道:使用JOIN或预加载
# 邪修:批量查询凑合用
def get_users_with_orders_hacky(user_ids):
users = User.query.filter(User.id.in_(user_ids)).all()
# 一次性查出所有订单,而不是每个用户查一次
all_orders = Order.query.filter(Order.user_id.in_(user_ids)).all()
# 手动组装(丑但有效)
orders_by_user = {}
for order in all_orders:
if order.user_id not in orders_by_user:
orders_by_user[order.user_id] = []
orders_by_user[order.user_id].append(order)
for user in users:
user.orders = orders_by_user.get(user.id, [])
return users
问题:事务死锁
python
# 正道:分析死锁原因,调整事务顺序,减小事务范围
# 邪修:重试大法
import time
from functools import wraps
def retry_on_deadlock(max_retries=3, delay=0.1):
def decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
for attempt in range(max_retries):
try:
return func(*args, **kwargs)
except DeadlockError:
if attempt < max_retries - 1:
time.sleep(delay * (attempt + 1)) # 指数退避
continue
raise
return wrapper
return decorator
@retry_on_deadlock(max_retries=5)
def transfer_money(from_account, to_account, amount):
# 可能死锁的操作
pass
2.2 API 的骚操作
问题:接口响应太慢
python
# 正道:优化数据库查询,使用缓存,异步处理
# 邪修:先返回,后处理
from fastapi import BackgroundTasks
@app.post("/process")
async def process_data(data: dict, background_tasks: BackgroundTasks):
# 立即返回,让用户觉得很快
task_id = generate_task_id()
# 实际处理放到后台
background_tasks.add_task(do_heavy_processing, task_id, data)
return {"status": "processing", "task_id": task_id}
# 用户:哇,好快!
# 实际上:还没开始处理呢...
# 更邪的:假进度条
@app.get("/task/{task_id}/progress")
async def get_progress(task_id: str):
actual_progress = get_real_progress(task_id)
# 让用户感觉一直在动
if actual_progress < 90:
fake_progress = min(actual_progress + random.randint(1, 5), 89)
else:
fake_progress = actual_progress
return {"progress": fake_progress}
问题:第三方 API 不稳定
python
# 正道:实现完善的重试机制、熔断器、降级策略
# 邪修:多备几个
class HackyApiClient:
def __init__(self):
self.endpoints = [
"https://api1.example.com",
"https://api2.example.com",
"https://api-backup.example.com",
]
def call_api(self, path, data):
last_error = None
for endpoint in self.endpoints:
try:
response = requests.post(
f"{endpoint}{path}",
json=data,
timeout=5
)
if response.ok:
return response.json()
except Exception as e:
last_error = e
continue # 这个不行,试下一个
# 全都不行?返回缓存的旧数据
cached = self.get_cached_response(path)
if cached:
return cached # 旧数据总比没数据好
raise last_error
# 终极邪修:硬编码兜底数据
DEFAULT_RESPONSE = {
"status": "ok",
"data": "服务暂时不可用,请稍后再试",
"is_fake": True # 至少诚实
}
2.3 缓存的黑魔法
python
# 问题:缓存和数据库不一致
# 正道:实现完善的缓存更新策略(Cache-Aside, Write-Through等)
# 邪修:双删策略
def update_user(user_id, data):
# 先删缓存
cache.delete(f"user:{user_id}")
# 更新数据库
db.update_user(user_id, data)
# 延迟再删一次(防止并发问题)
time.sleep(0.5) # 玄学数字
cache.delete(f"user:{user_id}")
# 更邪的:缓存永不过期,手动刷新
def get_user_hacky(user_id):
cached = cache.get(f"user:{user_id}")
if cached:
return cached
user = db.get_user(user_id)
cache.set(f"user:{user_id}", user) # 不设过期时间
return user
def refresh_user_cache(user_id):
"""需要刷新时手动调用"""
user = db.get_user(user_id)
cache.set(f"user:{user_id}", user)
# 终极邪修:出问题就清空所有缓存
def nuclear_option():
cache.flush_all() # 核弹级操作,慎用
第三章:调试邪修大全
3.1 当你找不到 Bug 在哪
javascript
// 正道:使用调试器,设置断点,分析调用栈
// 邪修:console.log大法
function mysteriousFunction(data) {
console.log("=== 进入函数 ===")
console.log("data:", data)
console.log("data类型:", typeof data)
console.log("data是否为null:", data === null)
console.log("data是否为undefined:", data === undefined)
const result = processData(data)
console.log("处理后:", result)
console.log("=== 离开函数 ===")
return result
}
// 进阶版:带颜色的console.log
const debug = {
log: (msg, data) => console.log(`%c${msg}`, "color: blue", data),
warn: (msg, data) => console.log(`%c${msg}`, "color: orange", data),
error: (msg, data) => console.log(`%c${msg}`, "color: red", data),
success: (msg, data) => console.log(`%c${msg}`, "color: green", data),
}
// 终极版:console.log地毯式轰炸
function debugEverything(obj, prefix = "") {
for (const key in obj) {
const value = obj[key]
const path = prefix ? `${prefix}.${key}` : key
console.log(`${path}:`, value)
if (typeof value === "object" && value !== null) {
debugEverything(value, path)
}
}
}
3.2 当 Bug 只在生产环境出现
javascript
// 正道:完善的日志系统,APM监控,错误追踪
// 邪修:远程调试注入
// 在生产代码里偷偷加一个后门(千万别真这么干)
if (
window.location.search.includes("debug=true") &&
document.cookie.includes("admin=true")
) {
// 开启详细日志
window.DEBUG_MODE = true
// 注入调试工具
const script = document.createElement("script")
script.src = "https://your-debug-tool.com/debug.js"
document.body.appendChild(script)
// 暴露内部状态
window.__APP_STATE__ = store.getState()
window.__DEBUG_TOOLS__ = {
getState: () => store.getState(),
dispatch: (action) => store.dispatch(action),
// 更多调试方法...
}
}
// 更安全的版本:条件编译
const isDev = process.env.NODE_ENV === "development"
const isDebug = isDev || localStorage.getItem("debug") === "true"
if (isDebug) {
window.onerror = (msg, url, line, col, error) => {
// 发送到你的调试服务器
fetch("/api/debug/error", {
method: "POST",
body: JSON.stringify({ msg, url, line, col, stack: error?.stack }),
})
}
}
3.3 当你不知道代码在哪被调用
javascript
// 正道:使用IDE的"查找引用"功能
// 邪修:打印调用栈
function whoCalledMe() {
console.log("调用栈:", new Error().stack)
}
// 更骚的:劫持函数
function spyOn(obj, methodName) {
const original = obj[methodName]
obj[methodName] = function (...args) {
console.log(`${methodName} 被调用了!`)
console.log("参数:", args)
console.log("调用者:", new Error().stack)
console.log("this:", this)
const result = original.apply(this, args)
console.log("返回值:", result)
return result
}
}
// 使用
spyOn(Array.prototype, "push")
// 现在每次调用 arr.push() 都会打印详细信息
// 终极版:Proxy监控一切
function createSpyObject(target, name = "object") {
return new Proxy(target, {
get(obj, prop) {
console.log(`${name}.${String(prop)} 被访问`)
const value = obj[prop]
if (typeof value === "function") {
return function (...args) {
console.log(`${name}.${String(prop)}() 被调用,参数:`, args)
return value.apply(obj, args)
}
}
return value
},
set(obj, prop, value) {
console.log(`${name}.${String(prop)} 被设置为:`, value)
obj[prop] = value
return true
},
})
}
第四章:性能优化邪修
4.1 前端性能邪修
javascript
// 问题:页面加载太慢
// 正道:代码分割、懒加载、优化资源
// 邪修:骨架屏 + 假数据
function App() {
const [data, setData] = useState(null)
const [showSkeleton, setShowSkeleton] = useState(true)
useEffect(() => {
// 先显示假数据,让用户觉得加载很快
setTimeout(() => {
setData(FAKE_DATA)
setShowSkeleton(false)
}, 100)
// 然后悄悄加载真数据
fetchRealData().then((realData) => {
setData(realData)
})
}, [])
if (showSkeleton) return <Skeleton />
return <Content data={data} />
}
// 更邪的:预测用户行为,提前加载
document.addEventListener("mousemove", (e) => {
const links = document.querySelectorAll("a")
links.forEach((link) => {
const rect = link.getBoundingClientRect()
const distance = Math.hypot(
e.clientX - (rect.left + rect.width / 2),
e.clientY - (rect.top + rect.height / 2)
)
// 鼠标靠近链接时,预加载目标页面
if (distance < 100) {
const href = link.getAttribute("href")
if (href && !prefetchedUrls.has(href)) {
prefetchPage(href)
prefetchedUrls.add(href)
}
}
})
})
4.2 后端性能邪修
python
# 问题:接口响应太慢
# 正道:优化算法、数据库查询、使用缓存
# 邪修:分页返回,假装很快
@app.get("/users")
async def get_users(page: int = 1, limit: int = 20):
# 不管有多少数据,每次只返回20条
users = db.query(User).offset((page - 1) * limit).limit(limit).all()
# 总数?先返回个假的,后台慢慢算
return {
"data": users,
"total": 10000, # 假的,但用户不知道
"page": page,
"has_more": len(users) == limit
}
# 更邪的:流式返回
from fastapi.responses import StreamingResponse
@app.get("/large-data")
async def get_large_data():
async def generate():
yield '{"data": ['
first = True
async for item in db.stream_query():
if not first:
yield ','
first = False
yield json.dumps(item)
yield ']}'
return StreamingResponse(generate(), media_type="application/json")
# 用户看到数据在"流动",感觉很快
# 终极邪修:异步处理 + 轮询
@app.post("/heavy-task")
async def start_heavy_task(data: dict):
task_id = str(uuid.uuid4())
# 立即返回
background_tasks.add_task(process_heavy_task, task_id, data)
return {"task_id": task_id, "status": "processing"}
@app.get("/heavy-task/{task_id}")
async def get_task_status(task_id: str):
status = get_task_status_from_redis(task_id)
if status == "completed":
return {"status": "completed", "result": get_task_result(task_id)}
elif status == "failed":
return {"status": "failed", "error": get_task_error(task_id)}
else:
# 返回假进度,让用户安心
fake_progress = min(get_real_progress(task_id) + 10, 95)
return {"status": "processing", "progress": fake_progress}
第五章:紧急救火邪修
5.1 生产环境出 Bug 了
python
# 场景:凌晨3点,生产环境崩了,老板在群里@你
# 正道:分析日志,定位问题,修复代码,测试,部署
# 邪修:先止血,再治病
# 方案1:功能开关
FEATURE_FLAGS = {
"new_payment_flow": False, # 出问题了,先关掉
"recommendation_engine": False, # 这个也关掉
"legacy_mode": True, # 回退到老版本
}
def process_payment(order):
if FEATURE_FLAGS["new_payment_flow"]:
return new_payment_flow(order) # 有Bug的新代码
else:
return legacy_payment_flow(order) # 虽然丑但能用的老代码
# 方案2:快速回滚
# 在部署脚本里
def emergency_rollback():
"""一键回滚到上一个稳定版本"""
os.system("kubectl rollout undo deployment/my-app")
# 或者
os.system("docker-compose down && docker-compose -f docker-compose.backup.yml up -d")
# 方案3:限流保命
from functools import wraps
import time
request_counts = {}
def rate_limit(max_requests=100, window=60):
def decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
now = time.time()
key = func.__name__
# 清理过期记录
request_counts[key] = [t for t in request_counts.get(key, []) if now - t < window]
if len(request_counts.get(key, [])) >= max_requests:
return {"error": "服务繁忙,请稍后再试"}, 429
request_counts.setdefault(key, []).append(now)
return func(*args, **kwargs)
return wrapper
return decorator
@rate_limit(max_requests=10, window=60) # 紧急限流
def problematic_endpoint():
pass
5.2 数据库被打爆了
python
# 场景:数据库CPU 100%,所有服务都在超时
# 正道:分析慢查询,优化索引,扩容
# 邪修:紧急止血
# 方案1:杀掉慢查询
def kill_slow_queries(threshold_seconds=30):
"""杀掉执行时间超过阈值的查询"""
slow_queries = db.execute("""
SELECT id, query, time
FROM information_schema.processlist
WHERE time > %s AND command = 'Query'
""", (threshold_seconds,))
for query in slow_queries:
try:
db.execute(f"KILL {query['id']}")
print(f"已杀掉查询: {query['id']}")
except:
pass
# 方案2:临时只读模式
class EmergencyReadOnlyMiddleware:
"""紧急只读模式,拒绝所有写操作"""
WRITE_METHODS = ['POST', 'PUT', 'DELETE', 'PATCH']
def __init__(self, app):
self.app = app
self.read_only = False
def __call__(self, environ, start_response):
if self.read_only and environ['REQUEST_METHOD'] in self.WRITE_METHODS:
start_response('503 Service Unavailable', [])
return [b'{"error": "System is in read-only mode for maintenance"}']
return self.app(environ, start_response)
# 方案3:降级到缓存
def get_user_with_fallback(user_id):
try:
# 先尝试从缓存获取
cached = cache.get(f"user:{user_id}")
if cached:
return cached
# 数据库查询(可能很慢或失败)
user = db.get_user(user_id)
cache.set(f"user:{user_id}", user, 3600)
return user
except Exception as e:
# 数据库挂了?返回缓存的旧数据
stale_cache = cache.get(f"user:{user_id}:stale")
if stale_cache:
return stale_cache
# 连旧缓存都没有?返回默认数据
return {"id": user_id, "name": "用户", "status": "unknown"}
5.3 内存泄漏了
javascript
// 场景:Node.js服务内存持续增长,即将OOM
// 正道:使用内存分析工具,找到泄漏点,修复代码
// 邪修:定时重启大法
// 在PM2配置里
module.exports = {
apps: [
{
name: "my-app",
script: "app.js",
max_memory_restart: "1G", // 内存超过1G就重启
cron_restart: "0 4 * * *", // 每天凌晨4点重启
},
],
}
// 更邪的:主动GC
// 启动时加 --expose-gc 参数
if (global.gc) {
setInterval(() => {
const before = process.memoryUsage().heapUsed
global.gc()
const after = process.memoryUsage().heapUsed
console.log(`GC回收了 ${(before - after) / 1024 / 1024} MB`)
}, 60000) // 每分钟强制GC一次
}
// 终极邪修:内存警报 + 自动重启
const MEMORY_THRESHOLD = 0.9 // 90%
setInterval(() => {
const usage = process.memoryUsage()
const heapUsedPercent = usage.heapUsed / usage.heapTotal
if (heapUsedPercent > MEMORY_THRESHOLD) {
console.error("内存即将耗尽,准备优雅重启...")
// 停止接收新请求
server.close(() => {
// 等待现有请求处理完
setTimeout(() => {
process.exit(0) // PM2会自动重启
}, 5000)
})
}
}, 10000)
第六章:代码复用邪修
6.1 复制粘贴的艺术
javascript
// 正道:抽象公共逻辑,创建可复用的模块
// 邪修:复制粘贴 + 微调
// 当你需要一个"差不多"的功能时
// 原始代码
function processUserData(user) {
// 100行复杂逻辑
}
// 邪修版本
function processAdminData(admin) {
// 复制上面100行
// 改几个变量名
// 加几个if判断
// 完美!
}
// 稍微不那么邪的版本:提取差异点
function processData(data, options = {}) {
const { type = "user", skipValidation = false, extraFields = [] } = options
// 公共逻辑
if (!skipValidation) {
validate(data)
}
// 根据类型处理差异
if (type === "admin") {
// admin特殊逻辑
}
// 更多公共逻辑
}
// 使用
processData(userData, { type: "user" })
processData(adminData, { type: "admin", skipValidation: true })
6.2 配置驱动开发
javascript
// 问题:类似的页面/功能太多,每个都要写一遍
// 邪修:配置化一切
const PAGE_CONFIGS = {
userList: {
title: '用户列表',
api: '/api/users',
columns: [
{ key: 'name', label: '姓名' },
{ key: 'email', label: '邮箱' },
{ key: 'status', label: '状态', render: (v) => v ? '启用' : '禁用' },
],
actions: ['edit', 'delete'],
filters: ['status', 'dateRange'],
},
orderList: {
title: '订单列表',
api: '/api/orders',
columns: [
{ key: 'orderNo', label: '订单号' },
{ key: 'amount', label: '金额', render: (v) => `¥${v}` },
{ key: 'status', label: '状态' },
],
actions: ['view', 'cancel'],
filters: ['status', 'dateRange', 'amount'],
},
// 再加100个类似的配置...
};
// 通用列表组件
function GenericListPage({ configKey }) {
const config = PAGE_CONFIGS[configKey];
const [data, setData] = useState([]);
useEffect(() => {
fetch(config.api).then(r => r.json()).then(setData);
}, [config.api]);
return (
<div>
<h1>{config.title}</h1>
<Filters config={config.filters} />
<Table columns={config.columns} data={data} />
<Actions config={config.actions} />
</div>
);
}
// 使用
<GenericListPage configKey="userList" />
<GenericListPage configKey="orderList" />
// 一个组件搞定所有列表页
第七章:邪修的边界------什么时候不能邪修
7.1 绝对不能邪修的场景
arduino
┌─────────────────────────────────────────────────────────────┐
│ 邪修禁区 🚫 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 💰 涉及金钱的代码 │
│ ├─ 支付逻辑 │
│ ├─ 账户余额计算 │
│ ├─ 订单金额处理 │
│ └─ 任何和钱相关的四舍五入 │
│ │
│ 🔐 涉及安全的代码 │
│ ├─ 用户认证 │
│ ├─ 权限校验 │
│ ├─ 密码处理 │
│ └─ 敏感数据加密 │
│ │
│ 📊 涉及数据一致性的代码 │
│ ├─ 分布式事务 │
│ ├─ 数据同步 │
│ ├─ 库存扣减 │
│ └─ 任何"不能出错"的业务 │
│ │
│ ⚖️ 涉及合规的代码 │
│ ├─ 审计日志 │
│ ├─ 数据留存 │
│ ├─ 隐私保护 │
│ └─ 任何可能被监管审查的功能 │
│ │
└─────────────────────────────────────────────────────────────┘
7.2 邪修的代价
javascript
// 邪修债务计算器
const technicalDebtCalculator = {
// 每种邪修的"利息"
debtRates: {
setTimeout解决时序问题: {
initialDebt: 1,
interestRate: 0.5, // 每月增加50%的维护成本
description: "迟早会出现更诡异的时序问题",
},
"!important覆盖样式": {
initialDebt: 0.5,
interestRate: 0.3,
description: "样式会越来越难改",
},
复制粘贴代码: {
initialDebt: 2,
interestRate: 1.0, // 每月翻倍
description: "改一个地方要改N个地方",
},
硬编码配置: {
initialDebt: 0.3,
interestRate: 0.2,
description: "换个环境就要改代码",
},
忽略错误处理: {
initialDebt: 3,
interestRate: 2.0,
description: "生产环境出问题时你会后悔的",
},
禁用ESLint警告: {
initialDebt: 0.5,
interestRate: 0.1,
description: "警告通常是有道理的",
},
},
// 计算N个月后的技术债务
calculateDebt(hackType, months) {
const { initialDebt, interestRate } = this.debtRates[hackType]
return initialDebt * Math.pow(1 + interestRate, months)
},
// 打印债务报告
printReport(hacks, months = 6) {
console.log(`\n📊 技术债务报告(${months}个月后)\n`)
console.log("=".repeat(50))
let totalDebt = 0
for (const hack of hacks) {
if (this.debtRates[hack]) {
const debt = this.calculateDebt(hack, months)
totalDebt += debt
console.log(`${hack}`)
console.log(
` 当前债务: ${this.debtRates[hack].initialDebt.toFixed(1)} 人天`
)
console.log(` ${months}个月后: ${debt.toFixed(1)} 人天`)
console.log(` 风险: ${this.debtRates[hack].description}`)
console.log()
}
}
console.log("=".repeat(50))
console.log(`总债务: ${totalDebt.toFixed(1)} 人天`)
console.log(`建议: ${totalDebt > 10 ? "🚨 尽快重构!" : "⚠️ 找时间清理"}`)
},
}
// 使用示例
technicalDebtCalculator.printReport(
["setTimeout解决时序问题", "复制粘贴代码", "忽略错误处理"],
6
)
7.3 邪修转正道的时机
javascript
// 什么时候应该把邪修代码重构成正道代码?
const refactorDecisionTree = {
shouldRefactor(context) {
const {
codeAge, // 代码存在多久了(月)
bugCount, // 这段代码产生了多少bug
changeFrequency, // 多久改一次
teamSize, // 团队多少人
isCoreBusiness, // 是否核心业务
} = context
// 决策逻辑
const reasons = []
if (codeAge > 3 && bugCount > 2) {
reasons.push("代码老旧且bug频发")
}
if (changeFrequency > 2 && teamSize > 3) {
reasons.push("频繁修改且多人协作")
}
if (isCoreBusiness && bugCount > 0) {
reasons.push("核心业务不能有隐患")
}
if (codeAge > 6) {
reasons.push("超过6个月的邪修代码应该重构")
}
return {
shouldRefactor: reasons.length >= 2,
reasons,
priority:
reasons.length >= 3 ? "HIGH" : reasons.length >= 2 ? "MEDIUM" : "LOW",
}
},
}
// 使用
const decision = refactorDecisionTree.shouldRefactor({
codeAge: 4,
bugCount: 3,
changeFrequency: 3,
teamSize: 5,
isCoreBusiness: true,
})
console.log(decision)
// {
// shouldRefactor: true,
// reasons: ['代码老旧且bug频发', '频繁修改且多人协作', '核心业务不能有隐患'],
// priority: 'HIGH'
// }
第八章:邪修工具箱
8.1 常用邪修代码片段
javascript
// 邪修工具箱 - 复制即用
// 1. 防抖(简易版)
const debounce = (fn, delay) => {
let timer
return (...args) => {
clearTimeout(timer)
timer = setTimeout(() => fn(...args), delay)
}
}
// 2. 节流(简易版)
const throttle = (fn, delay) => {
let last = 0
return (...args) => {
const now = Date.now()
if (now - last >= delay) {
last = now
fn(...args)
}
}
}
// 3. 深拷贝(够用版)
const deepClone = (obj) => JSON.parse(JSON.stringify(obj))
// 4. 随机ID
const randomId = () => Math.random().toString(36).substr(2, 9)
// 5. 睡眠函数
const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms))
// 6. 重试函数
const retry = async (fn, times = 3, delay = 1000) => {
for (let i = 0; i < times; i++) {
try {
return await fn()
} catch (e) {
if (i === times - 1) throw e
await sleep(delay)
}
}
}
// 7. 安全取值
const get = (obj, path, defaultValue) => {
const keys = path.split(".")
let result = obj
for (const key of keys) {
result = result?.[key]
if (result === undefined) return defaultValue
}
return result
}
// 8. 数组去重
const unique = (arr) => [...new Set(arr)]
// 9. 数组分组
const groupBy = (arr, key) =>
arr.reduce((acc, item) => {
const group = item[key]
acc[group] = acc[group] || []
acc[group].push(item)
return acc
}, {})
// 10. 格式化数字
const formatNumber = (num) =>
num.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",")
8.2 邪修注释模板
javascript
// 当你不得不写邪修代码时,至少留下注释
// TODO: 这是临时方案,需要在 [日期] 前重构
// HACK: 由于 [原因],这里使用了非常规方法
// FIXME: 这段代码有问题,但目前能用
// XXX: 这里有坑,小心!
// NOTE: 这样写是因为 [解释]
// OPTIMIZE: 性能不好,有空优化
// 完整模板
/**
* ⚠️ 邪修代码警告
*
* 为什么这样写:[解释原因]
* 已知问题:[列出问题]
* 正确做法:[描述正道方案]
* 重构计划:[日期] 前完成
* 负责人:[你的名字]
*
* 相关Issue:#123
*/
function hackyFunction() {
// 邪修代码
}
结语:邪修的哲学
写到最后,我想说几句认真的话。
邪修不是目的,是手段。
每一个邪修方案的背后,都有一个无奈的故事:
- 产品经理的 deadline
- 老板的"这个很简单吧"
- 客户的"我们付了钱的"
- 凌晨三点的生产事故
邪修的本质是权衡。
在"完美"和"能用"之间,在"优雅"和"按时交付"之间,在"最佳实践"和"现实约束"之间,做出选择。
但邪修有边界。
涉及安全、金钱、数据一致性的代码,绝对不能邪修。这是底线。
邪修需要记录。
每一个邪修方案都应该有注释,解释为什么这样做,以及正确的做法是什么。这是对未来的自己和同事的尊重。
邪修需要偿还。
技术债务是有利息的。今天省下的时间,未来会加倍偿还。所以,找时间把邪修代码重构成正道代码。
最后,送给所有程序员一句话:
"能用邪修解决的问题,说明你理解了问题的本质。能把邪修重构成正道,说明你掌握了解决问题的能力。"
愿你的代码永远不需要邪修。
但如果需要,希望这篇文章能帮到你。
附录:邪修速查表
| 场景 | 邪修方案 | 风险等级 | 正道方案 |
|---|---|---|---|
| 元素对不齐 | margin 负值微调 | 🟢 低 | Flexbox/Grid |
| 样式被覆盖 | !important | 🟡 中 | 提高选择器优先级 |
| 异步时序问题 | setTimeout | 🟡 中 | async/await |
| 组件不更新 | forceUpdate/key | 🟡 中 | 正确管理 state |
| 查询太慢 | 加索引/缓存 | 🟢 低 | 优化查询结构 |
| 接口超时 | 重试/降级 | 🟢 低 | 优化性能 |
| 内存泄漏 | 定时重启 | 🔴 高 | 找到泄漏点修复 |
| 生产 Bug | 功能开关/回滚 | 🟢 低 | 完善测试 |
本文由一个写过无数邪修代码的程序员撰写。
如果你的代码审查者问起这些技巧是从哪学的,请说"我自己想的"。
欢迎在评论区分享你的邪修经历------毕竟,每个程序员都有不能说的秘密。 🤫