【Software Engineering】Iterative Development, make it Work, then Better

软件开发模型系列(四):迭代开发 ------ 先让东西跑起来,再慢慢变好

前面三种模型(瀑布、V 模型、螺旋)有一个共同假设:项目有明确的起点和终点。但互联网时代来了------软件上线不是结束,而是开始。迭代开发改变了这个基本假设。


1、什么是迭代开发

迭代开发(Iterative Development) 的核心思想很简单:不要试图一次做对所有事情。先做一个能跑的简陋版本,然后通过一轮又一轮的反馈和改进,逐步逼近最终目标。
#mermaid-svg-3cZpBPJdHMhVZ6tk{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-3cZpBPJdHMhVZ6tk .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-3cZpBPJdHMhVZ6tk .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-3cZpBPJdHMhVZ6tk .error-icon{fill:#552222;}#mermaid-svg-3cZpBPJdHMhVZ6tk .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-3cZpBPJdHMhVZ6tk .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-3cZpBPJdHMhVZ6tk .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-3cZpBPJdHMhVZ6tk .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-3cZpBPJdHMhVZ6tk .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-3cZpBPJdHMhVZ6tk .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-3cZpBPJdHMhVZ6tk .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-3cZpBPJdHMhVZ6tk .marker{fill:#333333;stroke:#333333;}#mermaid-svg-3cZpBPJdHMhVZ6tk .marker.cross{stroke:#333333;}#mermaid-svg-3cZpBPJdHMhVZ6tk svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-3cZpBPJdHMhVZ6tk p{margin:0;}#mermaid-svg-3cZpBPJdHMhVZ6tk .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-3cZpBPJdHMhVZ6tk .cluster-label text{fill:#333;}#mermaid-svg-3cZpBPJdHMhVZ6tk .cluster-label span{color:#333;}#mermaid-svg-3cZpBPJdHMhVZ6tk .cluster-label span p{background-color:transparent;}#mermaid-svg-3cZpBPJdHMhVZ6tk .label text,#mermaid-svg-3cZpBPJdHMhVZ6tk span{fill:#333;color:#333;}#mermaid-svg-3cZpBPJdHMhVZ6tk .node rect,#mermaid-svg-3cZpBPJdHMhVZ6tk .node circle,#mermaid-svg-3cZpBPJdHMhVZ6tk .node ellipse,#mermaid-svg-3cZpBPJdHMhVZ6tk .node polygon,#mermaid-svg-3cZpBPJdHMhVZ6tk .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-3cZpBPJdHMhVZ6tk .rough-node .label text,#mermaid-svg-3cZpBPJdHMhVZ6tk .node .label text,#mermaid-svg-3cZpBPJdHMhVZ6tk .image-shape .label,#mermaid-svg-3cZpBPJdHMhVZ6tk .icon-shape .label{text-anchor:middle;}#mermaid-svg-3cZpBPJdHMhVZ6tk .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-3cZpBPJdHMhVZ6tk .rough-node .label,#mermaid-svg-3cZpBPJdHMhVZ6tk .node .label,#mermaid-svg-3cZpBPJdHMhVZ6tk .image-shape .label,#mermaid-svg-3cZpBPJdHMhVZ6tk .icon-shape .label{text-align:center;}#mermaid-svg-3cZpBPJdHMhVZ6tk .node.clickable{cursor:pointer;}#mermaid-svg-3cZpBPJdHMhVZ6tk .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-3cZpBPJdHMhVZ6tk .arrowheadPath{fill:#333333;}#mermaid-svg-3cZpBPJdHMhVZ6tk .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-3cZpBPJdHMhVZ6tk .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-3cZpBPJdHMhVZ6tk .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-3cZpBPJdHMhVZ6tk .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-3cZpBPJdHMhVZ6tk .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-3cZpBPJdHMhVZ6tk .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-3cZpBPJdHMhVZ6tk .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-3cZpBPJdHMhVZ6tk .cluster text{fill:#333;}#mermaid-svg-3cZpBPJdHMhVZ6tk .cluster span{color:#333;}#mermaid-svg-3cZpBPJdHMhVZ6tk div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-3cZpBPJdHMhVZ6tk .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-3cZpBPJdHMhVZ6tk rect.text{fill:none;stroke-width:0;}#mermaid-svg-3cZpBPJdHMhVZ6tk .icon-shape,#mermaid-svg-3cZpBPJdHMhVZ6tk .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-3cZpBPJdHMhVZ6tk .icon-shape p,#mermaid-svg-3cZpBPJdHMhVZ6tk .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-3cZpBPJdHMhVZ6tk .icon-shape .label rect,#mermaid-svg-3cZpBPJdHMhVZ6tk .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-3cZpBPJdHMhVZ6tk .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-3cZpBPJdHMhVZ6tk .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-3cZpBPJdHMhVZ6tk :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;}#mermaid-svg-3cZpBPJdHMhVZ6tk .stage>*{fill:#dbeafe!important;stroke:#2563eb!important;stroke-width:1px!important;color:#1e3a8a!important;}#mermaid-svg-3cZpBPJdHMhVZ6tk .stage span{fill:#dbeafe!important;stroke:#2563eb!important;stroke-width:1px!important;color:#1e3a8a!important;}#mermaid-svg-3cZpBPJdHMhVZ6tk .stage tspan{fill:#1e3a8a!important;} 基于上一轮反馈
基于上一轮反馈
第 3 轮 · V2.0
需求分析
设计
编码
测试
交付/反馈
第 2 轮 · V1.1
需求分析
设计
编码
测试
交付/反馈
第 1 轮 · V1.0
需求分析
设计
编码
测试
交付/反馈

和瀑布模式的关键区别:

text 复制代码
瀑布:一次交付整个系统,中间没有用户反馈

迭代:每次交付一个可运行的版本,不断根据反馈调整方向

2、迭代开发的历史:比你想象的早得多

很多人以为迭代开发是 2000 年代敏捷运动之后才出现的新东西。但事实上,迭代开发的实践比瀑布模型更早

时间 事件
1930s Walter Shewhart(贝尔实验室)提出统计过程控制和持续改进循环的概念(后被 Deming 发展为 PDCA/PDSA 循环)
1950s W. Edwards Deming 将 Shewhart 的循环推广到日本工业界,深刻影响了丰田生产系统
1960s NASA 的水星计划(Project Mercury)使用迭代开发
1971 IBM 的 Harlan Mills 提出"软件应该通过增量开发来成长"
1977-80 IBM 团队用 17 轮迭代(每轮约 8 周)开发了航天飞机的主飞行控制软件
1986 / 1995 Frederick Brooks 在《没有银弹》(1986)中主张"软件应增量式地生长(grow, don't build)";1995 年在《人月神话》20 周年纪念版中更直接宣称"瀑布模型是错的"
1990s Rational Unified Process(RUP)将迭代开发系统化、商业化

历史的有趣之处:迭代开发不是"发明出来替代瀑布的",它一直就存在。瀑布模型在 1970-1990 年代的大规模流行,反而是一段历史上的弯路------很大程度上要归因于美国国防部 DOD-STD-2167 标准对 Royce 论文的误读。


3、迭代 vs 增量:一个重要区分

这两个词经常被混用(包括在很多教材中),但它们指向的是两个不同的维度:

维度 迭代(Iterative) 增量(Incremental)
关注点 时间维度:重复同一个过程 产品维度:叠加新的功能
工作方式 同一个功能反复打磨、改进 每次增加一个完整的新功能块
比喻 画家画一幅肖像,先画轮廓,再逐步细化 盖房子一间一间盖,每间盖好就是成品
核心问题 "我们做对了吗?"(学习与修正) "接下来交付什么?"(功能分解)

在实际项目中,两者往往是结合的:

text 复制代码
微信的发展:

  迭代的维度 → 聊天功能从文字→语音→视频,不断打磨
  
  增量的维度 → 先有聊天,再加朋友圈,再加支付,再加小程序

现代 Scrum 框架(详见本系列第六篇)实际上同时包含了这两个维度:每个 Sprint 是一个迭代 (重复相同的仪式和周期),而每个 Sprint 结束时交付的是一个增量(可用的产品功能切片)。


4、迭代开发的核心优势

4.1、风险前置,而不是后置

text 复制代码
瀑布:               迭代:
需求 设计 编码 测试   V1: 需求→设计→编码→测试→反馈
                     ↓
最大的风险在最后       V2: 需求→设计→编码→测试→反馈
才发现                 ↓
                     V3: ...
                     
                     最大的技术风险和需求理解风险在 V1 就被发现了

4.2、用户可以尽早看到产品

这带来的好处远超"让用户开心":

  • 用户可以在看到实际产品后给出更准确的反馈(而不是对着需求文档凭空想象)
  • 错误的产品方向在投入还不多的时候就会被纠正
  • 用户可以边用边学,需求质量逐轮提高

4.3、团队可以在过程中学习

text 复制代码
第一轮:大家还在磨合,代码质量一般

第二轮:对业务更理解了,架构可以调整

第三轮:找到了最佳实践,效率大幅提升

瀑布模型的一次性开发剥夺了团队这种学习机会。


5、迭代开发的挑战

5.1、架构容易退化

如果每一轮只关注"这轮要交付的功能",很容易出现:
#mermaid-svg-aHooLrFGvImfWx9z{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-aHooLrFGvImfWx9z .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-aHooLrFGvImfWx9z .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-aHooLrFGvImfWx9z .error-icon{fill:#552222;}#mermaid-svg-aHooLrFGvImfWx9z .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-aHooLrFGvImfWx9z .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-aHooLrFGvImfWx9z .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-aHooLrFGvImfWx9z .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-aHooLrFGvImfWx9z .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-aHooLrFGvImfWx9z .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-aHooLrFGvImfWx9z .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-aHooLrFGvImfWx9z .marker{fill:#333333;stroke:#333333;}#mermaid-svg-aHooLrFGvImfWx9z .marker.cross{stroke:#333333;}#mermaid-svg-aHooLrFGvImfWx9z svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-aHooLrFGvImfWx9z p{margin:0;}#mermaid-svg-aHooLrFGvImfWx9z .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-aHooLrFGvImfWx9z .cluster-label text{fill:#333;}#mermaid-svg-aHooLrFGvImfWx9z .cluster-label span{color:#333;}#mermaid-svg-aHooLrFGvImfWx9z .cluster-label span p{background-color:transparent;}#mermaid-svg-aHooLrFGvImfWx9z .label text,#mermaid-svg-aHooLrFGvImfWx9z span{fill:#333;color:#333;}#mermaid-svg-aHooLrFGvImfWx9z .node rect,#mermaid-svg-aHooLrFGvImfWx9z .node circle,#mermaid-svg-aHooLrFGvImfWx9z .node ellipse,#mermaid-svg-aHooLrFGvImfWx9z .node polygon,#mermaid-svg-aHooLrFGvImfWx9z .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-aHooLrFGvImfWx9z .rough-node .label text,#mermaid-svg-aHooLrFGvImfWx9z .node .label text,#mermaid-svg-aHooLrFGvImfWx9z .image-shape .label,#mermaid-svg-aHooLrFGvImfWx9z .icon-shape .label{text-anchor:middle;}#mermaid-svg-aHooLrFGvImfWx9z .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-aHooLrFGvImfWx9z .rough-node .label,#mermaid-svg-aHooLrFGvImfWx9z .node .label,#mermaid-svg-aHooLrFGvImfWx9z .image-shape .label,#mermaid-svg-aHooLrFGvImfWx9z .icon-shape .label{text-align:center;}#mermaid-svg-aHooLrFGvImfWx9z .node.clickable{cursor:pointer;}#mermaid-svg-aHooLrFGvImfWx9z .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-aHooLrFGvImfWx9z .arrowheadPath{fill:#333333;}#mermaid-svg-aHooLrFGvImfWx9z .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-aHooLrFGvImfWx9z .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-aHooLrFGvImfWx9z .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-aHooLrFGvImfWx9z .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-aHooLrFGvImfWx9z .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-aHooLrFGvImfWx9z .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-aHooLrFGvImfWx9z .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-aHooLrFGvImfWx9z .cluster text{fill:#333;}#mermaid-svg-aHooLrFGvImfWx9z .cluster span{color:#333;}#mermaid-svg-aHooLrFGvImfWx9z div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-aHooLrFGvImfWx9z .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-aHooLrFGvImfWx9z rect.text{fill:none;stroke-width:0;}#mermaid-svg-aHooLrFGvImfWx9z .icon-shape,#mermaid-svg-aHooLrFGvImfWx9z .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-aHooLrFGvImfWx9z .icon-shape p,#mermaid-svg-aHooLrFGvImfWx9z .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-aHooLrFGvImfWx9z .icon-shape .label rect,#mermaid-svg-aHooLrFGvImfWx9z .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-aHooLrFGvImfWx9z .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-aHooLrFGvImfWx9z .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-aHooLrFGvImfWx9z :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;}#mermaid-svg-aHooLrFGvImfWx9z .stage>*{fill:#dbeafe!important;stroke:#2563eb!important;stroke-width:1px!important;color:#1e3a8a!important;}#mermaid-svg-aHooLrFGvImfWx9z .stage span{fill:#dbeafe!important;stroke:#2563eb!important;stroke-width:1px!important;color:#1e3a8a!important;}#mermaid-svg-aHooLrFGvImfWx9z .stage tspan{fill:#1e3a8a!important;}#mermaid-svg-aHooLrFGvImfWx9z .pain>*{fill:#fee2e2!important;stroke:#dc2626!important;stroke-width:1px!important;color:#7f1d1d!important;}#mermaid-svg-aHooLrFGvImfWx9z .pain span{fill:#fee2e2!important;stroke:#dc2626!important;stroke-width:1px!important;color:#7f1d1d!important;}#mermaid-svg-aHooLrFGvImfWx9z .pain tspan{fill:#7f1d1d!important;} 第 1 轮:快速上线,架构随意 → 能跑
第 2 轮:加新功能,打补丁 → 还能跑
第 3 轮:再加功能,更多补丁 → 勉强能跑
第 4 轮:架构崩溃,需要重写 → 跑不动了

这就是"技术债"的积累。迭代开发需要比瀑布模型更强的架构自律------每次迭代都花时间重构和还技术债。

5.2、对用户的时间和参与度要求更高

迭代模式需要用户持续参与每个版本的反馈。如果用户组织"太忙了,下一版再看",反馈循环就会断裂,迭代开发的核心优势就丧失了。


6、迭代开发 vs 瀑布模型:一个具体对比

假设开发一个电商 APP,预计要 6 个月:

text 复制代码
瀑布模式:
  月1-2:需求分析(产出 200 页 PRD)
  月3-4:系统设计
  月5:编码
  月6:测试 + 上线
  → 用户在第 6 个月才第一次看到 APP

迭代模式:
  月1-2:第1轮(注册登录 + 商品浏览)→ 用户试用
  月3-4:第2轮(搜索 + 购物车 + 下单)→ 用户试用
  月5:第3轮(支付 + 订单管理)→ 用户试用
  月6:第4轮(评价 + 推荐 + 优化)→ 正式上线
  → 用户在第一个月就能看到可运行的 APP

如果用户在第 2 轮反馈"搜索体验太差了,我们需要全文搜索而不是简单的关键词匹配"------在迭代模式下,第 3 轮就可以调整。在瀑布模式下,等你听到这个反馈,所有代码都写完了。


7、本篇要点速记

text 复制代码
迭代开发 = 分轮交付 + 持续反馈 + 逐步完善

历史比瀑布模型更久远(NASA 1960s 就在用)。

关键区分:迭代(反复打磨)vs 增量(逐块叠加)

优点 → 风险前置、用户早参与、团队可学习

局限 → 架构退化、需要用户持续参与

一句话:不要一次把东西做好,要先把东西做出来,再把它做好。

8、迭代开发模型 vs 螺旋模型

迭代模型关注的是"产品如何成长",螺旋模型关注的是"风险如何降低"。

Iterative Development 的第一性原理:没有人第一次就能把产品做好

产品质量不断逼近目标。迭代开发 = Continuous Improvement(持续改进)


螺旋模型第一性原理

螺旋模型认为第一性原理:真正的问题不是怎么开发?而是我根本不知道这个项目有没有坑

每绕一圈风险越来越少。不是功能越来越多。而是未知越来越少。

关键词:Risk Reduction(风险降低)

螺旋包含了迭代,螺旋把"风险分析"放到了每一次迭代最前面。把最大的坑先踩出来。

text 复制代码
                软件开发

                    │
        ┌───────────┴───────────┐
        │                       │
   Product Growth          Risk Control
 (产品成长)              (风险控制)
        │                       │
        ▼                       ▼
 Iterative Development      Spiral Model

螺旋模型 = 风险驱动的迭代开发(Risk-Driven Iterative Development)

两者看起来都在"一轮一轮地做事",但驱动力完全不同:迭代开发以"产品演进"为中心,螺旋模型以"风险演进"为中心。

也正因为如此,可以把螺旋模型理解为一种以风险管理为核心的迭代开发方法------它保留了迭代的形式,但重新定义了每一轮迭代最优先要解决的问题。


上一篇:螺旋模型

下一篇:敏捷开发 ------ 从"按计划行事"到"拥抱变化"

相关推荐
郝学胜-神的一滴6 小时前
CMake 038:OBJECT目标复用编译+Linux动态库版本自动化管理
linux·c++·程序人生·软件工程·软件构建·cmake·工程配置
Being--1 天前
BWorkflow:给人 + Claude Code 团队用的项目交付“规则层”
软件工程·agent·ai coding
qq_171538852 天前
两条赛道,同一个未来:计算机科学与技术 VS 软件工程
大数据·计算机外设·软件工程
嘿黑嘿呦19 天前
chap 8排序
算法·蓝桥杯·排序算法·软件工程
旧曲重听119 天前
2026前端技术从「夯」到「拉」
前端·程序人生·职场和发展·软件工程
承渊政道19 天前
飞算JavaAI 智能引导背后的多 Agent 协作机制解析:从老旧 Java 后台升级到可运行工程
java·开发语言·spring boot·安全·intellij-idea·软件工程·ai编程
apcipot_rain20 天前
计科八股20260616(1)——堆存中位数、链表判环、黑白测试、敏捷开发与瀑布模型、配置管理、持续集成、池化
数据结构·算法·软件工程
lisw0520 天前
【计算机科学技术】路由器(route):概念、历史、内容与战略!
机器学习·智能路由器·软件工程
培培说证20 天前
大数据、人工智能、计算机、软件工程,到底怎么选?
大数据·人工智能·软件工程