前言:AI 真的很快,但老项目不怕慢,怕乱

这两年,AI 写代码的速度越来越快。

以前我们写一个接口,可能要自己查文档、翻旧代码、复制模板、改字段、调试半天。现在你把需求丢给 AI,它几秒钟就能生成一段看起来很完整的 C# 代码:有方法、有异常处理、有注释,甚至还会帮你封装工具类。

但如果你真的维护过企业老项目,尤其是 WinForms、WebForms、ASMX、.NET Framework、SQL Server 2008 这种技术栈,你就会发现一个很现实的问题:

AI 写出来的代码,不是不能用,而是不能直接复制。

因为老项目最复杂的地方,往往不是语法,而是环境、兼容性、历史包袱、业务规则、线程模型、数据库版本、部署方式,以及那些没人敢随便动的“祖传逻辑”。

AI 能帮你把代码写快,但它不一定知道你项目里的坑。


一、AI 生成的代码,最大的问题不是“错”,而是“看起来很对”

很多人用 AI 写代码,最容易中招的地方就在这里:

它写出来的代码经常结构清晰、命名规范、注释完整,看起来比自己项目里的老代码还漂亮。

但是漂亮不等于能跑,能跑也不等于能上线。

比如你让 AI 写一个 C# 文件上传接口,它可能给你生成这样的代码:

using var stream = new FileStream(path, FileMode.Create);
await file.CopyToAsync(stream);

这段代码在新项目里很正常,但如果你的项目是 .NET Framework 4.5、C# 6.0、WebForms 或 ASMX 老接口,那就可能直接编译失败。

原因很简单:

using var 是 C# 8.0 的语法,老项目不一定支持。
async/await 虽然在较早版本已经支持,但老项目里如果调用链、异常处理、页面生命周期、同步上下文处理不好,也可能引发新的问题。
IFormFile 是 ASP.NET Core 常见类型,WebForms/ASMX 老项目里根本没有这个东西。

所以 AI 最大的危险不是明显写错,而是它经常写出一种“新项目正确、老项目不适配”的代码。


二、老项目不能直接复制 AI 代码的第一个原因:语言版本不兼容

很多企业老项目还停留在 .NET Framework 4.x,甚至编译环境还是 Visual Studio 2017、旧版 MSBuild、旧版 NuGet 包。

这种情况下,AI 很容易生成高版本 C# 语法,比如:

using var conn = new SqlConnection(connStr);

var result = list?.FirstOrDefault()?.Name ?? "默认值";

public string Name { get; init; }

var user = request switch
{
    "admin" => "管理员",
    "user" => "普通用户",
    _ => "未知"
};

这些写法在新项目里很舒服,但老项目未必能编译。

如果你是维护 .NET Framework 老项目,尤其是 C# 6.0、C# 7.0 左右的项目,就要特别提醒 AI:

请生成兼容 .NET Framework 4.5 / C# 6.0 的代码。
不要使用 using var、switch 表达式、record、init、nullable reference types、top-level statements。

这句话非常关键。

否则你会发现,AI 生成的代码看起来很高级,但你一复制到项目里,编译器直接一片红。


三、第二个原因:AI 不知道你的项目依赖了哪些老组件

老项目里经常有很多历史依赖,比如:

  • DevExpress 17.2

  • Newtonsoft.Json 老版本

  • M2Mqtt

  • LitJson

  • WebForms / ASMX

  • SQL Server 2008

  • 自定义控件库

  • 公司内部封装的 AppClient、SqlHelper、CacheData

  • 老版本的 WinForms 框架

AI 如果不知道这些上下文,就容易生成“看起来先进,但项目用不了”的方案。

比如你让 AI 写一个 JSON 返回,它可能默认用:

return Ok(new { code = 0, message = "success", data = result });

但这是 ASP.NET Web API / ASP.NET Core 的风格。

你的 ASMX 老项目里可能根本没有 Ok(),你真正需要的是:

JsonWriteOut(json);

或者:

Context.Response.ContentType = "application/json";
Context.Response.Write(json);
Context.Response.End();

再比如你让 AI 写 SQL 查询,它可能生成:

OFFSET 0 ROWS FETCH NEXT 20 ROWS ONLY

但 SQL Server 2008 不支持这种分页语法。

这就是老项目最麻烦的地方:
不是你不会写新代码,而是项目环境不允许你随便用新东西。


四、第三个原因:AI 不懂你的业务规则

代码只是表面,业务才是核心。

比如你让 AI 写一个“删除订单”的方法,它可能会直接写:

DELETE FROM Orders WHERE OrderID = @OrderID

但真实业务里可能不能这么干。

因为订单可能关联:

  • 支付记录

  • 发票记录

  • 物流记录

  • 售后记录

  • 操作日志

  • 用户余额

  • 第三方回调状态

所以真实项目里,删除可能不是物理删除,而是逻辑删除:

UPDATE Orders SET IsDeleted = 1 WHERE OrderID = @OrderID

甚至还要判断订单状态:

UPDATE Orders 
SET IsDeleted = 1 
WHERE OrderID = @OrderID 
AND Status IN ('Draft', 'Cancelled')

AI 能写代码,但它不知道你们公司当初为什么这么设计。

老项目里很多看起来奇怪的代码,背后可能都是历史事故。

有些代码不是没人会改,而是不敢改。
不是它写得丑,而是它承载了很多隐性的业务规则。


五、第四个原因:WinForms 老项目最怕线程模型被 AI 写乱

如果你做过 WinForms,就一定知道一个问题:

后台线程不能直接操作 UI 控件。

比如 MQTT 收到消息、WebSocket 收到事件、呼叫中心来电弹屏、后台任务下载文件,这些大概率都发生在非 UI 线程。

AI 可能会生成这样的代码:

private void OnMessageReceived(string msg)
{
    lblStatus.Text = msg;
    gridControl1.DataSource = GetData();
}

看起来没问题,但如果这个事件来自后台线程,就可能报跨线程异常:

Cross-thread operation not valid

正确做法应该先判断是否需要切回 UI 线程:

private void SafeSetStatus(string msg)
{
    if (this.InvokeRequired)
    {
        this.BeginInvoke(new Action<string>(SafeSetStatus), msg);
        return;
    }

    lblStatus.Text = msg;
}

但是也不能无脑 Invoke
如果后台线程疯狂触发事件,比如 MQTT 断线重连、WebSocket 重复登录、数据库日志高频写入,就算你用了 Invoke,也可能把 UI 消息队列塞爆,导致界面假死。

所以 WinForms 老项目里,AI 生成的代码必须重点检查:

  • 是否跨线程操作控件

  • 是否可能造成 UI 阻塞

  • 是否在 UI 线程执行耗时任务

  • 是否存在重复事件触发

  • 是否存在无限重连

  • 是否可能高频写数据库日志

  • 是否使用了同步等待导致死锁

AI 可以帮你写 Invoke,但它不一定知道你的事件一秒钟会触发多少次。


六、第五个原因:数据库代码不能直接信 AI

C# 老项目里,SQL 往往是重灾区。

AI 生成 SQL 代码时,常见问题有几个:

1. 直接拼接 SQL,存在注入风险

错误示例:

string sql = "SELECT * FROM esApp_User WHERE fldPhone = '" + phone + "'";

如果 phone 是用户输入的,就有 SQL 注入风险。

更稳妥的写法应该是参数化查询:

string sql = "SELECT * FROM esApp_User WHERE fldPhone = @phone";

SqlParameter[] parameters = new SqlParameter[]
{
    new SqlParameter("@phone", phone)
};

2. 生成了 SQL Server 2008 不支持的语法

比如:

OFFSET 0 ROWS FETCH NEXT 20 ROWS ONLY

SQL Server 2008 老项目里要考虑 ROW_NUMBER() 分页:

SELECT *
FROM
(
    SELECT 
        ROW_NUMBER() OVER (ORDER BY fldCreateTime DESC) AS RowNum,
        *
    FROM esApp_Post
) T
WHERE T.RowNum BETWEEN 1 AND 20

3. 忽略字段长度和空值

AI 很容易生成:

row["fldUserName"].ToString()

但如果字段是 DBNull,或者前端传来的字符串超过数据库字段长度,就可能报错。

老项目里必须养成习惯:

string userName = row["fldUserName"] == DBNull.Value 
    ? "" 
    : row["fldUserName"].ToString();

更重要的是,保存前要做长度截断、必填校验、类型校验。

很多线上问题不是代码不会写,而是没有处理脏数据。


七、第六个原因:AI 不知道你的异常应该怎么处理

很多 AI 生成的异常处理是这样的:

try
{
    // 业务代码
}
catch (Exception ex)
{
    throw ex;
}

或者:

catch (Exception ex)
{
    Console.WriteLine(ex.Message);
}

这在企业项目里都不合适。

throw ex; 会破坏原始堆栈信息。
Console.WriteLine 在 WinForms、WebForms、Windows 服务里通常没有意义。
直接把异常信息返回给前端,又可能泄露数据库表名、服务器路径、SQL 语句等敏感信息。

更合理的方式是:

try
{
    // 业务代码
}
catch (Exception ex)
{
    AppClient.SaveLog("System", Guid.Empty, "接口异常", ex.ToString());
    JsonWriteOut("{}", 500, "系统繁忙,请稍后再试");
}

但是这里也要注意:日志不能无限写。

如果一个异常一秒钟触发几十次,全部写数据库,数据库也可能被拖慢。
所以高频异常要考虑日志降级,比如:

  • 同类错误一分钟只写一次

  • 连续失败后进入熔断状态

  • 本地文件日志和数据库日志分级

  • 严重错误再写数据库

  • 普通重试不要每次都写完整堆栈

AI 生成代码时,很少主动考虑这些运行时压力。


八、第七个原因:AI 很容易“重构过度”

很多人让 AI 优化老代码,AI 一上来就建议:

  • 拆分项目

  • 引入依赖注入

  • 改成 Repository 模式

  • 加 AutoMapper

  • 改成异步

  • 升级到 ASP.NET Core

  • 用 EF Core 替代 ADO.NET

  • 用微服务拆分

这些建议不是错,但不一定适合当前项目。

老项目改造最忌讳“一步到位”。

如果一个 WinForms 项目已经跑了十年,里面有大量业务逻辑、报表、硬件对接、串口、呼叫中心、MQTT、数据库缓存,你直接大规模重构,风险极高。

更稳妥的方式是小步改造:

  1. 先补日志

  2. 再补参数校验

  3. 再抽公共方法

  4. 再拆业务层

  5. 再做接口标准化

  6. 再做单元测试或回归测试

  7. 最后才考虑架构升级

老项目改造不是炫技,而是外科手术。

目标不是让代码看起来新,而是让系统更稳定、更容易维护、更不容易出事故。


九、AI 代码复制进老项目前,我建议做这 10 项检查

每次从 AI 那里拿到 C# 代码,不要马上复制进项目。

先检查这 10 个问题:

1. 语言版本是否兼容?

是否用了:

  • using var

  • record

  • init

  • switch 表达式

  • nullable reference types

  • top-level statements

  • pattern matching 高版本语法

如果项目是 C# 6.0 或旧版编译环境,先让 AI 改成兼容写法。

2. 框架是否匹配?

确认代码适用于:

  • WinForms

  • WebForms

  • ASMX

  • Windows Service

  • .NET Framework

  • ASP.NET Core

不要把 ASP.NET Core 的代码直接复制进 ASMX。

3. 第三方包是否项目已有?

不要轻易让 AI 引入新包。

老项目引入一个新包,可能带来:

  • 版本冲突

  • Newtonsoft.Json 冲突

  • 运行时缺 DLL

  • 服务器无法部署

  • 其他模块异常

4. 是否有 SQL 注入风险?

所有用户输入进入 SQL,都要优先考虑参数化。

5. 是否兼容数据库版本?

SQL Server 2008、2012、2016、2019 支持的语法不完全一样。
不要默认所有 SQL 都能跑。

6. 是否处理 DBNull?

DataTable、DataRow、DataSet 老项目里,空值处理非常关键。

7. 是否可能阻塞 UI 线程?

WinForms 项目必须检查耗时操作有没有放到 UI 线程里。

8. 是否跨线程操作控件?

后台事件更新 UI 必须通过 InvokeBeginInvoke

9. 是否破坏原有业务规则?

不要只看代码能不能跑,要看是否符合业务状态流转。

10. 是否有回滚方案?

上线前必须知道:如果这段代码出问题,怎么恢复?


十、给 AI 的正确提示词:不要只说“帮我写代码”

很多人用 AI 效果不好,是因为提示词太简单。

比如:

帮我写一个 C# 上传图片接口。

这种提示词太泛了,AI 大概率按新项目写。

你应该这样写:

你现在是一个有 10 年经验的 C# 企业项目维护工程师。

请帮我写一个图片上传接口,要求:

1. 项目环境是 .NET Framework 4.5,C# 6.0
2. 后端是 WebForms / ASMX,不是 ASP.NET Core
3. 数据库是 SQL Server 2008
4. 不要使用 using var、record、init、switch 表达式等高版本语法
5. 不要引入新第三方包
6. 所有 SQL 必须使用参数化查询
7. 返回 JSON 使用统一结构:code、message、data
8. 必须包含 try-catch
9. 必须兼容老项目部署方式
10. 请解释每段代码的作用,并指出可能的风险点

如果是 WinForms 代码,可以这样问:

你现在是资深 C# WinForms 工程师。

请帮我分析下面这段代码:

1. 是否存在 UI 线程阻塞
2. 是否存在跨线程操作控件
3. 是否存在死循环重连
4. 是否存在高频写数据库日志
5. 是否可能导致界面假死
6. 请给出兼容 .NET Framework 4.5 / C# 6.0 的修改方案
7. 不要使用高版本语法
8. 修改时尽量少动原有业务逻辑

这类提示词比“帮我优化代码”有效得多。

因为你把项目边界告诉 AI,它才不容易胡乱发挥。


十一、AI 在老项目里最适合做什么?

虽然我一直说不能直接复制 AI 代码,但这不代表 AI 没用。

恰恰相反,AI 对老项目非常有价值,只是用法要对。

我认为 AI 最适合做这几类事:

1. 读懂老代码

让 AI 帮你解释一个几百行的方法,梳理调用链、变量含义、业务流程。

2. 找潜在风险

让 AI 检查空引用、数组越界、SQL 注入、线程安全、异常吞掉、资源未释放。

3. 生成兼容版本代码

前提是你明确告诉它项目版本和语法限制。

4. 写技术文档

老项目最缺文档。
AI 可以帮你把接口、字段、业务流程整理成说明文档。

5. 做代码审查

让 AI 站在资深工程师角度,指出这段代码上线前还需要检查什么。

6. 辅助排查异常

把异常堆栈、关键代码、运行环境给 AI,它可以快速帮你缩小范围。

但要记住:
AI 是副驾驶,不是最终责任人。

代码上线后出问题,背锅的不是 AI,而是开发者。


十二、老项目真正需要的不是“更快写代码”,而是“更稳地交付”

AI 的出现,让写代码这件事变快了。

但企业老项目真正难的地方,从来不只是写代码。

真正难的是:

  • 需求到底是不是这样

  • 老数据会不会出问题

  • 用户操作路径会不会冲突

  • 数据库字段够不够

  • 老版本客户端能不能兼容

  • 部署服务器有没有对应环境

  • 异常日志能不能追踪

  • 出问题能不能回滚

  • 这段代码三个月后别人能不能看懂

AI 可以让你一天写出以前三天的代码量,但如果没有测试、没有规范、没有边界、没有回滚,写得越快,风险可能越大。

对于老项目来说,真正值钱的能力不是“我能让 AI 帮我生成多少代码”,而是:

我知道哪些代码不能直接用,哪些地方必须人工判断,哪些业务规则不能动,哪些风险必须提前兜住。

这才是老项目工程师的价值。


不要拒绝 AI,也不要迷信 AI

我不反对用 AI 写代码。

相反,我认为程序员必须学会用 AI。

但在 C# 老项目、WinForms、WebForms、ASMX、SQL Server 2008 这种企业级存量系统里,AI 生成的代码只能作为初稿,不能当成最终答案。

真正成熟的做法是:

让 AI 提效,让人来兜底。

AI 负责生成方案、整理思路、补充代码、解释逻辑。
开发者负责判断兼容性、业务正确性、运行风险、上线影响。

以后程序员拼的可能不再是谁敲代码快,而是谁更懂业务、更懂系统边界、更懂风险控制、更懂如何把 AI 变成自己的工程能力。

代码可以让 AI 写得更快。
但系统能不能稳定运行,最终还是看人。

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐