刚接触后端开发那会儿,我接手了一个 PHP 项目。打开代码的那一刻,我以为自己在考古。3000 行的 index.php,SQL 语句和 HTML 混在一起,全局变量满天飞。这不是代码,这是一碗意大利面。

一个真实的噩梦

让我带你看看那份代码长什么样:

<?php
$conn = mysql_connect("localhost", "root", "123456");
mysql_select_db("mydb", $conn);

if ($_GET['action'] == 'list') {
    $sql = "SELECT * FROM users WHERE status = 1";
    $result = mysql_query($sql);
    echo "<html><body><table>";
    while ($row = mysql_fetch_array($result)) {
        echo "<tr><td>" . $row['name'] . "</td>";
        echo "<td>" . $row['email'] . "</td>";
        if ($row['role'] == 'admin') {
            echo "<td><a href='?action=delete&id=" . $row['id'] . "'>删除</a></td>";
        }
        echo "</tr>";
    }
    echo "</table></body></html>";
} elseif ($_GET['action'] == 'delete') {
    $sql = "DELETE FROM users WHERE id = " . $_GET['id'];
    mysql_query($sql);
    header("Location: ?action=list");
}
// ... 还有 2900 多行
?>

问题清单

问题危害
SQL 注入漏洞$_GET['id'] 直接拼接 SQL,可以删库
明文密码数据库密码硬编码在代码里
无分层数据库、业务、视图全部混在一起
无复用每个页面都重复写连接数据库
难测试想测试?只能打开浏览器点点点

这不是个例。这是那个时代许多小公司的常态。

混沌的特征

什么是「混沌」?不是代码少,是代码没有边界

混沌的本质:任何代码可以访问任何数据,任何改动可能影响任何地方。

特征一:全局变量满天飞

// config.php
$db_host = "localhost";
$db_user = "root";
$current_user = null;
$is_admin = false;
$cart_items = array();
$error_message = "";

每个文件都 include 'config.php',每个文件都能修改这些变量。

结果:你永远不知道变量在哪里被改了。调试一个 bug,可能要翻遍整个项目。

特征二:复制粘贴编程

需要在另一个页面显示用户列表?复制一份。

需要在后台也显示?再复制一份。

三个月后,项目里有 17 份「略有不同」的用户列表代码。改一个 bug,要改 17 个地方。漏掉一个,就是新 bug。

复制粘贴是技术债务的复利。

特征三:业务逻辑散落各处

想知道「用户下单」的逻辑?你需要看:

  • order.php 里有一部分
  • cart.php 里有一部分
  • payment.php 里有一部分
  • email.php 里有发送通知
  • 数据库触发器里还有一部分

没有人知道完整的流程。 包括写这些代码的人。

特征四:改动恐惧症

每次改代码都像拆炸弹:

  • 这个函数被谁调用了?不知道
  • 改这个变量会影响什么?不知道
  • 这段代码为什么这样写?不知道(注释说的是另一回事)

最安全的策略是:不改。 但需求不会停。于是你选择:在外面包一层。

六个月后,包了六层。代码变成了俄罗斯套娃。

混沌是怎么形成的?

没有人故意写烂代码。混沌是日积月累的结果

阶段 1:原型期(一切都很美好)

项目刚开始,需求简单,一个文件搞定一切。代码清晰、直接、高效。

「不需要过度设计,先跑起来再说。」

阶段 2:功能膨胀(开始变味)

需求越来越多。文件从 100 行变成 500 行,从 500 行变成 2000 行。

「没时间重构,先加上这个功能。」

阶段 3:人员更替(开始失控)

原作者离职了。新人接手,看不懂代码,不敢改。于是:

  • 能复制就复制
  • 能绕过就绕过
  • 能不碰就不碰

阶段 4:死亡螺旋(积重难返)

代码越来越难改 → 改动越来越慢 → 压力越来越大 → 越来越没时间重构 → 代码更难改

技术债务会产生利息。而且是复利。

一个类比:房子的装修

想象你买了一套毛坯房。

第一年:简单装修,能住就行。线路裸露在外,但能用。

第二年:加装空调,电线不够,接个插线板。

第三年:加装洗碗机,水管不够,从厨房接一根管到阳台。

第四年:加装新风系统,没地方走管,凿墙。

第五年:想改造?水电工打开墙:「这是谁接的线?我不敢动,怕整栋楼停电。」

这就是混沌代码的真实写照。

每一次「临时方案」都在增加复杂度。当复杂度超过某个临界点,系统就变成了「遗留系统」——能跑,但没人敢碰。

混沌的代价

代价一:开发效率断崖式下降

阶段新功能开发时间
第 1 个月2 天
第 6 个月1 周
第 12 个月2 周
第 24 个月1 个月

同样的功能,后期开发时间可能是前期的数倍甚至十几倍。 具体倍数因项目而异,但趋势是明确的:技术债务会拖慢一切。

代价二:Bug 率指数级增长

改一个 bug,引入两个新 bug。因为你不知道改动会影响什么。

代价三:人员流动加剧

混沌代码会加速人员流动。新人上手困难,老人疲于应付。

当维护成本超过重写成本,团队士气会受到影响。

代价四:创新停滞

「这个功能做不了,代码不支持。」

「这个改动风险太大,下个版本再说。」

技术开始拖业务的后腿。

当时的「解决方案」

面对混沌,那个年代的程序员们也尝试过一些办法:

方案 1:文件夹分类

/project
├── user/
│   ├── list.php
│   ├── add.php
│   └── delete.php
├── order/
│   ├── list.php
│   └── create.php
└── admin/
    └── dashboard.php

结果:文件是分开了,但代码风格还是一样。每个 list.php 里还是数据库、业务、HTML 混在一起。

换汤不换药。

方案 2:include 大法

<?php
include 'header.php';
include 'db.php';
include 'functions.php';
// 业务逻辑...
include 'footer.php';
?>

结果functions.php 变成了垃圾场。什么函数都往里扔,最后变成 5000 行的「工具类」。

方案 3:「框架」

技术总监说:「我们需要一个框架。」

于是他写了一个「框架」:

<?php
class Controller {
    function run() {
        $action = $_GET['action'] ?? 'index';
        $this->$action();
    }
}

class UserController extends Controller {
    function index() {
        // 还是数据库、业务、HTML 混在一起...
    }
}
?>

有了 Controller,但没有 Model,没有 View。 披着框架的皮,干着面条的事。

走出混沌的希望

2005 年,Ruby on Rails 发布了。它带来了一个革命性的理念:

Convention over Configuration(约定优于配置)

Rails 告诉你:

  • 模型放 app/models/
  • 控制器放 app/controllers/
  • 视图放 app/views/
  • 数据库操作用 ActiveRecord,不要写 SQL

不是你决定代码怎么组织,是框架告诉你怎么组织。

同一年,Django(Python)发布。同样的理念。

再后来,Laravel(PHP)发布,把现代框架的理念带回了 PHP 世界。

混沌的终结,需要自律与约束的结合。

良好的开发习惯(自律)和框架强制的规范(约束)缺一不可。框架提供外部约束,但团队的工程文化同样重要。

从混沌中学到的教训

回顾那段混沌时期,我学到了几个重要的教训:

教训 1:代码需要边界

任何代码都应该有明确的职责。一段代码做一件事,做好一件事。

教训 2:「先跑起来」是陷阱

快速原型可以不讲究,但原型应该被扔掉,而不是演变成产品。

教训 3:技术债务会吃人

现在省下的时间,未来要十倍偿还。而且是带利息的。

教训 4:框架不是可选项

不是「要不要用框架」的问题,而是「用什么框架」的问题。对于通用 Web 开发,成熟的开源框架通常是更好的选择。自研框架在特定领域(如高频交易、游戏引擎)可能是合理的,但需要充分评估成本。

总结

混沌时代的本质

  • 没有边界,代码可以访问任何东西
  • 没有分层,业务和技术混在一起
  • 没有约束,想怎么写就怎么写

混沌的代价

  • 开发效率断崖式下降
  • Bug 率指数级增长
  • 人才流失、创新停滞

走出混沌的关键

  • 需要边界(分层)
  • 需要约定(规范)
  • 需要框架(约束)

混沌不是错误,是演进的起点。没有经历过混沌,就不会珍惜秩序。


下一篇,我们来看 Django/Rails 带来的 MVC 革命——框架如何用「约定」拯救了混乱的代码世界。


下一篇:MVC 启蒙:框架带来的秩序

本系列:

  1. 混沌时代:当代码没有架构(本篇)
  2. MVC 启蒙:框架带来的秩序
  3. Context 之道:从技术分层到业务分层
  4. DDD 觉醒:让代码说业务的语言
  5. 边界的艺术:六边形与洋葱
  6. 单体的边界:何时该拆?
  7. 微服务与云原生:分布式的代价
  8. 没有银弹:架构决策的本质