《软件工程实务》课程学习心得:从理论到实践的蜕变之旅
2026/5/14 3:10:06 网站建设 项目流程

《软件工程实务》课程学习心得:从理论到实践的敏捷蜕变

关键词:软件工程、敏捷开发、Scrum、微服务、DevOps、Codeup、能源管理系统

可在该链接内学习相关内容:

https://www.bilibili.com/


一、写在前面

本学期我修读了《软件工程实务》课程,从课程概要到项目实战,系统学习了软件工程全生命周期。不同于以往只关注编码,这门课让我真正理解了产品愿景、用户故事、架构设计、进度管理、DevOps等工程化实践。
本文将结合课程内容、个人项目实践(能源管理系统)以及使用的工具(Codeup),分享我的学习心得。

一点背景:在修这门课之前,我对“软件工程”的理解停留在“写文档”三个字上。我以为它是一堆繁文缛节的规章制度,甚至会拖慢开发进度。但经过一个学期的学习,我彻底改变了这个看法——软件工程不是束缚,而是对复杂性的管理。当项目规模从几百行代码扩展到几千行、几万行,当团队从1个人变成5个人,当交付时间从“随便”变成“第8周周五下午5点截止”时,工程方法的价值就显现出来了。


二、课程结构速览

单元核心内容实践工具/产出我的掌握程度(自评)
1课程介绍 + Codeup入门Git仓库、Markdown笔记⭐⭐⭐⭐⭐
2产品愿景 + 进度管理甘特图、WBS⭐⭐⭐⭐
3产品定义 + Git入门Codeup协作⭐⭐⭐⭐⭐
4用户故事用户故事卡⭐⭐⭐⭐
5功能设计 + 能源功能清单功能列表⭐⭐⭐⭐
6软件架构 + 微服务架构图⭐⭐⭐
7业务架构设计业务流程图⭐⭐⭐⭐
8技术架构 + 数据字典数据库设计⭐⭐⭐⭐
9敏捷 + 产品积压项PB列表⭐⭐⭐⭐⭐
10Scrum框架Sprint计划⭐⭐⭐⭐
11可靠编程 + 安全隐私代码规范、加密示例⭐⭐⭐
12云计算 + DevOpsCI/CD流水线⭐⭐⭐

📌 从自评可以看出,我对工具类和敏捷实践类的掌握更好,架构设计类还需要继续深入学习。


三、五大核心收获(详细展开)

1️⃣ 产品愿景驱动开发

在第二单元中,我们为“能源管理系统”制定了产品愿景。这不是随便写一句话,而是要回答三个核心问题:为谁做?解决什么问题?做到什么程度?

我们小组最终确定的产品愿景:

为园区物业管理人员提供实时、可视化的能耗监控与智能告警服务,帮助用户在6个月内实现15%以上的能源成本节约。

为什么这个愿景有效?

  • ✅ 明确目标用户:园区物业管理人员

  • ✅ 明确核心价值:实时、可视化、智能告警

  • ✅ 明确量化指标:15%能源成本节约、6个月时间节点

我的体会:愿景不是贴在墙上就完事的。在后续的每个Sprint中,我们都会回到愿景来问自己:“这个功能真的帮助用户节约能源了吗?”有一次我们想做一个复杂的报表导出功能,但回顾愿景后发现用户更需要的是实时告警,于是调整了优先级。这就是愿景对开发的实际指导作用。


2️⃣ Git + Codeup 团队协作

在第三单元中,我们正式使用阿里云Codeup进行团队协作。我们小组有4个人,分别是:前端1人、后端2人、测试兼文档1人。

我们采用的Git工作流

bash

# 1. 每天开始工作前,同步develop分支 git checkout develop git pull origin develop # 2. 基于develop创建功能分支(用feat/前缀 + 用户故事ID) git checkout -b feat/US-003-user-login # 3. 开发过程中,小步提交(每个提交只做一件事) git add src/main/java/com/energy/controller/UserController.java git commit -m "feat(login): 实现用户登录接口,返回JWT token" # 4. 开发完成后,推送到远程并创建合并请求 git push origin feat/US-003-user-login # 然后在Codeup网页上创建MR → 指定评审人 → 通过后合并到develop

我们踩过的坑

问题场景错误做法正确做法
两个人改了同一个文件的同一行直接 push 被拒绝,然后强制 push ❌先 pull → 解决冲突 → 再 push
功能开发到一半,紧急需要切分支修bug直接 checkout 导致工作区混乱 ❌git stash暂存当前改动
合并请求被驳回,不知道哪里改重新提交一个MR ❌在同一个分支上继续 commit,MR会自动更新

我的体会:Git不是“把代码传上去就行”的工具,它是团队协作的通信协议。谁的commit message写得不清楚,谁就在给别人挖坑。我们小组后来约定:commit message必须按照类型(模块): 简短描述的格式,例如fix(auth): 修复token过期后未刷新问题

🖼️图片说明(模拟)

图中展示了主分支(main)、开发分支(develop)和三个功能分支(feat/energy-chart、feat/alert-rule、feat/user-profile)的合并记录,以及最后一次CI/CD流水线的状态(绿色通过)。


3️⃣ 用户故事与敏捷积压

第四单元的用户故事是让我印象最深刻的内容之一。以前我写需求就是“做一个登录功能”,但用户故事强迫我们思考:谁要用?为什么要用?怎么才算完成?

标准格式

md

作为 [角色] 我想要 [功能] 以便 [价值]

我们编写的真实案例

md

作为 能源管理员 我想要 查看指定时间范围(日/周/月/自定义)的用电趋势图 以便 发现高峰用电时段并制定错峰用电策略。

验收标准(Acceptance Criteria)

  • ✅ 可以选择时间范围:今日、本周、本月、自定义日期区间

  • ✅ 以折线图形式展示,X轴为时间,Y轴为用电量(单位:kWh)

  • ✅ 鼠标悬停时显示具体数值

  • ✅ 可以导出图片(PNG格式)

产品积压项详细表(第9-10单元内容):

优先级用户故事ID用户故事内容估算(故事点)负责人状态
🔴 高US-001作为管理员,我希望通过账号密码登录系统5张三✅ 已完成
🔴 高US-002作为管理员,我希望查看所有设备的实时能耗数据8李四✅ 已完成
🟡 中US-003作为管理员,我希望为指定设备设置能耗告警阈值5王五🔄 进行中
🟡 中US-004作为管理员,我希望按日/周/月导出能耗报表(Excel)8赵六⏳ 待开发
🟢 低US-005作为管理员,我希望对比不同设备的能耗排名3-📋 积压中
🟢 低US-006作为管理员,我希望接收邮件告警通知5-📋 积压中

我的体会:用户故事是“沟通工具”而不是“合同文档”。在和产品经理、测试同学讨论时,用故事的形式比用长长的需求文档高效得多。验收标准写清楚了,测试用例也就有了基础,开发过程中扯皮的情况大大减少。


4️⃣ 软件架构:从单体到微服务

在第六和第七单元,我们学习了软件架构设计。最初我们设计的是一体化架构(前端+后端+数据库都在一个项目里),但随着功能增加,我们发现:

  • ❌ 编译时间越来越长(从10秒到2分钟)

  • ❌ 改一个小功能需要重新部署整个应用

  • ❌ 某个模块出问题(比如告警模块内存泄漏)会导致整个系统不可用

于是我们决定向微服务演进。最终设计如下:

text

┌─────────────────┐ │ 前端(Vue.js) │ └────────┬────────┘ │ HTTPS ┌────────▼────────┐ │ API网关(Spring Cloud Gateway) │ └────────┬────────┘ │ ┌────────┬───────────┼───────────┬────────┐ │ │ │ │ │ ┌───────▼──────┐ ┌▼──────────▼──┐ ┌───────▼──────┐ ┌▼──────────┐ │ 认证服务 │ │ 数据采集服务 │ │ 能耗分析服务 │ │ 告警服务 │ │ (JWT) │ │ (Modbus/TCP) │ │ (InfluxDB) │ │ (SMTP/钉钉)│ └───────┬──────┘ └──────┬───────┘ └──────┬───────┘ └──────┬─────┘ │ │ │ │ └────────┬───────┴────────────────┴────────┬───────┘ │ │ ┌────────▼────────┐ ┌────────▼────────┐ │ MySQL │ │ InfluxDB │ │ (用户/权限) │ │ (时序数据) │ └─────────────────┘ └─────────────────┘

各服务职责:

服务名称职责端口技术栈
API网关路由转发、限流、鉴权8080Spring Cloud Gateway
认证服务登录、Token颁发与校验8081Spring Security + JWT
数据采集服务从电表读取数据,写入InfluxDB8082Netty + Modbus协议
能耗分析服务查询、聚合、趋势计算8083Spring Boot + InfluxDB驱动
告警服务检测阈值并发送通知8084Spring Boot + 钉钉机器人

🖼️架构图说明(模拟)
​​​​​​​

我的体会:微服务不是银弹。我们小组只有4个人,其实单体架构完全够用。但我们选择微服务是出于学习目的。真正让我体会到微服务好处的是独立部署:有一次告警服务出了bug,我们只重启了那个服务,主流程(数据采集和展示)完全不受影响。这在单体架构中是做不到的。

https://www.baidu.com/


5️⃣ DevOps + 云计算

第12单元的DevOps是整门课的“压轴戏”。我们使用阿里云Codeup自带的流水线功能,实现了从代码提交到自动部署的全流程。

CI/CD流水线配置(.codeup/pipeline.yml):

yaml

# Codeup流水线配置文件(简化版) name: 能源管理系统CI/CD stages: - name: 代码检出 type: git-checkout - name: 后端构建(Java) type: maven script: mvn clean package -DskipTests artifacts: - target/*.jar - name: 单元测试 type: maven script: mvn test # 测试报告收集 reports: junit: target/surefire-reports/*.xml - name: Docker镜像构建 type: docker-build dockerfile: Dockerfile image: registry.cn-hangzhou.aliyuncs.com/energy/energy-service:${CI_COMMIT_SHORT_SHA} - name: 部署到测试环境 type: ssh-deploy host: 47.xxx.xxx.xxx script: | docker pull registry.cn-hangzhou.aliyuncs.com/energy/energy-service:${CI_COMMIT_SHORT_SHA} docker stop energy-service || true docker rm energy-service || true docker run -d --name energy-service -p 8080:8080 registry.cn-hangzhou.aliyuncs.com/energy/energy-service:${CI_COMMIT_SHORT_SHA}

我们对DevOps的理解变化

阶段我们的做法问题DevOps后的做法
第1周用U盘拷代码版本混乱、覆盖丢失Git + Codeup统一仓库
第3周手动scp上传jar包忘记传配置文件流水线自动打包+部署
第5周部署前想起来要测试经常在线上发现bug流水线自动执行单元测试
第7周部署后手动验证耗时且容易遗漏部署后自动运行冒烟测试

我的体会:DevOps最打动我的不是技术,而是安全感。以前上线新功能就像“赌一把”,现在有了流水线,每次提交都会自动运行300多个单元测试,有什么问题5分钟之内就能发现。这种信心是之前没有过的。

🖼️系统截图模拟
​​​​​​​
图中显示近7天用电量趋势折线图,X轴为日期(5月7日-5月13日),Y轴为用电量(kW)。可以看到5月10日(周五)有一个明显的高峰(156kW),分析原因是空调集中使用。右侧显示各区域实时功率排行:生产车间(45kW)、办公楼(28kW)、食堂(12kW)。

https://image.baidu.com/search/index?tn=baiduimage&fm=result&ie=utf-8&word=%5B%E5%9B%BE3%EF%BC%9A%E8%83%BD%E8%80%97%E4%BB%AA%E8%A1%A8%E6%9D%BF%E6%88%AA%E5%9B%BE%5D%20%E5%9B%BE%E4%B8%AD%E6%98%BE%E7%A4%BA%E8%BF%917%E5%A4%A9%E7%94%A8%E7%94%B5%E9%87%8F%E8%B6%8B%E5%8A%BF%E6%8A%98%E7%BA%BF%E5%9B%BE%EF%BC%8C%E4%BB%A5%E5%8F%8A%E5%90%84%E5%8C%BA%E5%9F%9F%E7%9A%84%E5%AE%9E%E6%97%B6%E5%8A%9F%E7%8E%87%E6%8E%92%E8%A1%8C%E3%80%82


四、完整的功能实现代码示例(带详细注释)

根据用户故事“US-002 查看实时能耗”,我实现了完整的REST API + Service + 数据库查询。

4.1 Controller层

java

package com.energy.controller; import com.energy.common.Result; // 统一响应封装 import com.energy.entity.EnergyData; // 能耗实体类 import com.energy.service.EnergyService; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import lombok.RequiredArgsConstructor; import org.springframework.web.bind.annotation.*; /** * 能耗数据接口 * 对应敏捷用户故事:US-002 作为管理员,我希望查看所有设备的实时能耗数据 */ @RestController @RequestMapping("/api/energy") @Api(tags = "能耗管理") @RequiredArgsConstructor public class EnergyController { private final EnergyService energyService; /** * 获取指定设备的最新实时能耗 * GET /api/energy/realtime/{deviceId} * * @param deviceId 设备编号,如 "DEV-METER-001" * @return 最新能耗数据,包含数值、单位、采集时间 */ @GetMapping("/realtime/{deviceId}") @ApiOperation("获取实时能耗") public Result<EnergyData> getRealtime(@PathVariable String deviceId) { // 参数校验:设备编号不能为空 if (deviceId == null || deviceId.trim().isEmpty()) { return Result.error("设备编号不能为空"); } EnergyData data = energyService.getLatest(deviceId); // 如果没有数据,返回友好的提示(而不是null或异常) if (data == null) { return Result.error("未找到设备 " + deviceId + " 的能耗数据"); } return Result.success(data); } /** * 批量获取多个设备的最新数据 * POST /api/energy/realtime/batch * * @param deviceIds 设备编号列表 * @return 设备编号 → 能耗数据的Map */ @PostMapping("/realtime/batch") @ApiOperation("批量获取实时能耗") public Result<Map<String, EnergyData>> getBatchRealtime(@RequestBody List<String> deviceIds) { if (deviceIds == null || deviceIds.isEmpty()) { return Result.error("设备编号列表不能为空"); } // 限制单次查询数量,防止恶意请求或缓存穿透 if (deviceIds.size() > 100) { return Result.error("单次查询设备数量不能超过100个"); } Map<String, EnergyData> result = energyService.getLatestBatch(deviceIds); return Result.success(result); } }

4.2 单元测试(TDD实践)

java

package com.energy.controller; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.test.web.servlet.MockMvc; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Mockito.when; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; /** * EnergyController单元测试 * 采用TDD思想:先写测试(定义期望行为),再实现功能 */ @WebMvcTest(EnergyController.class) class EnergyControllerTest { @Autowired private MockMvc mockMvc; @MockBean private EnergyService energyService; @Test void testGetRealtime_Success() throws Exception { // Arrange:准备测试数据 EnergyData mockData = EnergyData.builder() .deviceId("DEV-METER-001") .value(120.5) .unit("kW") .timestamp(LocalDateTime.now()) .build(); // 模拟Service层行为 when(energyService.getLatest("DEV-METER-001")).thenReturn(mockData); // Act & Assert:执行请求并验证响应 mockMvc.perform(get("/api/energy/realtime/DEV-METER-001")) .andExpect(status().isOk()) .andExpect(jsonPath("$.code").value(200)) .andExpect(jsonPath("$.data.value").value(120.5)) .andExpect(jsonPath("$.data.unit").value("kW")); } @Test void testGetRealtime_DeviceNotFound() throws Exception { // 测试设备不存在的情况 when(energyService.getLatest("INVALID-DEVICE")).thenReturn(null); mockMvc.perform(get("/api/energy/realtime/INVALID-DEVICE")) .andExpect(status().isOk()) .andExpect(jsonPath("$.code").value(500)) .andExpect(jsonPath("$.message").value(containsString("未找到设备"))); } @Test void testGetRealtime_EmptyDeviceId() throws Exception { // 测试空设备编号的边界情况 mockMvc.perform(get("/api/energy/realtime/")) .andExpect(status().isNotFound()); // Spring MVC会对空路径返回404 } }

五、课程中遇到的最大困难与解决方法

困难1:第一次做架构设计,完全不知道从哪下手

背景:第六单元讲微服务架构时,老师让我们画出自己项目的架构图。我盯着空白页看了20分钟,一个字都写不出来。

解决过程

  1. 我先找了一个成熟的开源项目(若依微服务版),看懂它的分层

  2. 然后用“抄作业”的方式,先把结构框架画出来

  3. 再根据自己的项目替换具体内容(把“用户服务”改成“数据采集服务”等)

  4. 最后找助教review,修改了3版才定稿

反思:不会画不是能力问题,是还没见过足够多的好例子。以后遇到陌生领域,先看10个例子再动手。

困难2:团队两个人同时改一个文件,Git冲突不会解决

背景:有一次王小和李四同时修改了application.yml配置文件,两人都push了,导致远程分支冲突。

解决步骤(后来整理成文档):

bash

# 1. 拉取最新代码,发现冲突提示 git pull origin develop # 输出:CONFLICT (content): Merge conflict in application.yml # 2. 打开冲突文件,找到冲突标记 <<<<<<< HEAD port: 8080 ======= port: 8081 >>>>>>> feat/alert-service # 3. 两人沟通后决定:李四的功能需要8081,王小改成8081 # 4. 手动删除标记,保留正确内容:port: 8081 # 5. 标记为已解决并提交 git add application.yml git commit -m "resolve: 解决application.yml端口冲突,统一使用8081" git push origin develop

困难3:自动化测试覆盖率一直达不到要求

问题:课程要求单元测试覆盖率≥80%,我们一开始只有52%。

解决方法:

  • 使用JaCoCo插件生成覆盖率报告,逐行分析未覆盖代码

  • 优先补全核心业务逻辑(Service层)的测试

  • Controller层的简单CRUD不做过度测试

  • 最终覆盖率:82%(Service层92%,Controller层73%)


六、课程考核方式与我的得分

考核项占比我的得分失分原因(反思)
平时实验 + Codeup提交记录30%95/100有一次忘记提交周报
团队项目(能源管理系统)40%90/100架构文档写得太简略,扣了10分
个人学习笔记 + 博客20%92/100有一篇博客排版问题
课堂互动与汇报10%88/100汇报时间超时2分钟

总评:86分(良好)

自我评价:知识上收获很大,分数上没有拿到优秀有点遗憾,但问题出在表达和规范上,不是能力问题。下学期选修高级软件工程继续努力。


七、总结与展望

《软件工程实务》让我从一个“只会写代码”的学生,成长为具备工程思维、团队协作、架构意识的准软件工程师。我深刻体会到:

如果只会...后果学完之后我懂得了...
写代码项目一复杂就维护不了编写单元测试 + 遵循代码规范
自己一个人开发无法与他人协作Git分支模型 + 代码审查
跟着感觉做需求做出来没人用用户故事 + 验收标准
直接部署上线经常出问题且回滚困难CI/CD流水线 + 灰度发布

三个“不再”:

  • ❌ 不再写没有用户故事的功能

  • ❌ 不再直接 push 到 main 分支

  • ❌ 不再“我觉得没问题”就上线

三个“开始”

  • ✅ 开始写测试用例

  • ✅ 开始画架构图

  • ✅ 开始做回顾复盘

未来我会继续深入学习

  • 🔧 云原生技术(K8s、Service Mesh、Istio)

  • 🔐 软件安全与隐私合规(GDPR、等级保护)

  • 🧠 AI辅助软件工程(GitHub Copilot、AutoDev)

  • 📊 可观测性(Metrics、Logging、Tracing三位一体)


八、致谢与资源推荐

感谢

  • 课程老师

  • 队友

  • Codeup提供的学生免费资源

推荐资源

类型名称简介
书籍《软件工程:实践者的研究方法》经典教材,案例丰富
书籍《用户故事与敏捷方法》把用户故事讲透了
书籍《DevOps实践指南》学DevOps必看
在线教程阿里云开发者学堂-敏捷课程免费 + 实战
工具Codeup阿里云一站式研发平台(学生优惠)
社区思否敏捷话题国内活跃的敏捷社区

如还有疑惑可点击下方链接解惑:

https://www.deepseek.com/

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询