玩游戏代码=:}高效敏捷方法
为何当你的分行是沙箱? —? 和作为合法工作玩耍?
热辣辣椒 多数开发商都试图在发展的各个阶段做到完美,
一. 导言导言 导言 导言 导言
I' 一直在为"... 好了,,"而专业地建立软件 只是说我记得 当""Agile""是一个新想法
这些年来,, I' 已经注意到一些事情 : 播放权限最差的代码来自每个键盘都觉得被仔细检查过的项目 , 我试图从第一分钟开始 写"
这是意外事件 . 创意与压力
因此,I'已经开发出一种方法 将乱七八糟的,创意探索与光滑的,生产区分开来让它运作起来,让它漂亮一点@" @ @ - @ 但其实是, @ it@'}关于允许自己在没有完美重量的情况下进行实验,
三阶段
让我先说清楚 : 这是关于草率或避免纪律的 在适当的时候运用正确的纪律.
graph LR
subgraph "Phase 1: Make It Work"
A[New Requirement] --> B[Explore & Experiment]
B --> C{Does It Work?}
C -->|No| D[Try Different Approach]
D --> B
C -->|Yes| E[Basic Solution]
end
subgraph "Phase 2: Make It Pretty"
E --> F[Refactor & Clean]
F --> G[Apply SOLID Pragmatically]
G --> H[Get Stakeholder Feedback]
H --> I[Polished Solution]
end
subgraph "Phase 3: Lock It Down"
I --> J[Add Tests]
J --> K[Security Review]
K --> L[Add Monitoring]
L --> M[Production Ready]
end
style A stroke:#f59e0b,stroke-width:3px
style E stroke:#0ea5e9,stroke-width:3px
style I stroke:#8b5cf6,stroke-width:3px
style M stroke:#10b981,stroke-width:4px
阶段"1: 使它发挥作用
咒语: 首先播放“.”确认您的假设 “.” 获取正在运行的“MS K2”
这就是你的分支是沙箱的阶段 MSMK0 误差代码是好的 . 罚款“. ”您不是在这里建教堂吗?“;” 您是在餐巾纸上画的草图
你试图找出什么 :
- 此 API 实际上是否表现得如何, 医生们声称 @? @ @ ( @Spoiler@: @ 它很少做 @MS K3}
- 我能用我拥有的工具来解决这个问题吗?
- 实际要求是多少?
- 这是两个时间问题还是两周问题?
关键洞察力 直到你升起一个PL, 你的分行就是 您 的I',但每次在Optechnology , 承诺和推动. Hook 进入你的CI输油管时 没有人需要看到你的假启动吗? , 你的评论=-}解调代码@, *你的评论"*TODO}这太可怕了
附带利益=: 常务是心跳.在远程MS K1async队}♪,穩定的足迹"{"但进展中〞"承诺让每个人都放心,
这个心理自由是必需的 @. 当你知道你可以把一切都扔掉, 并开始新鲜的@,}你就会做出更大胆的决定. 你尝试那种可能赢得'_t working}.}有时它会很成功 不是一个特质.
// Phase 1 code looks like this - and THAT'S OKAY
public async Task<Result> ProcessThing(Request req)
{
// TODO: what if this is null?
var data = await _api.GetSomething(req.Id);
// This probably isn't right but let's see what happens
var transformed = data.Items
.Where(x => x.Status != "deleted") // Is this the right filter?
.Select(x => new Thing { Name = x.Name }); // Missing loads of fields
// HACK: hardcoded for now
return new Result { Items = transformed.ToList(), Total = 42 };
}
这是生产代码吗? ?? 上帝没有! 它有用吗?
关于反馈的说明:* 您可以在下列时间获得反馈 任何 正在选择的密钥 人 或 目标是建立最佳功能... ...,,不要遵循僵硬的流程.
建立另一个团队将消费的API, 另一个团队将会消耗#?}让它们参与进来 早点做一个. 一个快速的“"%does”这个形状就有意义了..."?"}对话可以节省重新工作的日子 外观 而不是它是否 工程@.}他们在「2.」的阶段里提供反馈。
使用您的判断@. @% 有时您需要反馈 澄清@ , @ 不批准@ MS K1 @ spec 已做此决定: @ . @ @ I"_ spect 上面写着“ '} handle 差错 优雅地” @'— @does, 意思是重试三次+, @ 或快速失败, 然后通知@?"@ that'_ 是一个阶段 @ MPK9# talking MSG10 @
如果一个利益攸关方@' @%s 输入是关键, 但是他们与粗略代码“ ,” 的争斗会构建一个微不足道的演示 < . > a hardcoded happy*-path, 它能显示这个概念可以解开有价值的反馈, 而不会分散缺失边缘案例的注意力}
切换实用性:.+B 目标为最佳特征 @,_BARBAR_纯度进程=.}
阶段2: 使它美丽
咒语: 现在你知道自己在建什么了了吗? 良好.
探索阶段已经完成了它的工作 “.你 '你确认了这个方法的功效 你理解问题的形状“,” 而您可能发现了十几个边缘病例 最初的票号是"'T提到'.′
现在你把它清理干净吧 :
// Phase 2: Same logic, but actually maintainable
public async Task<ProcessingResult> ProcessItemsAsync(
ProcessingRequest request,
CancellationToken cancellationToken = default)
{
ArgumentNullException.ThrowIfNull(request);
var items = await _itemRepository.GetActiveItemsAsync(
request.AccountId,
cancellationToken);
var processedItems = items
.Where(item => item.Status != ItemStatus.Deleted)
.Select(item => _mapper.ToProcessedItem(item))
.ToList();
return new ProcessingResult
{
Items = processedItems,
TotalCount = processedItems.Count,
ProcessedAt = _timeProvider.UtcNow
};
}
这里就是你的 :
- 适用SOLID原则 注重实际 {\fn黑体\fs22\bord1\shad0\3aHBE\4aH00\fscx67\fscy66\2cHFFFFFF\3cH808080}但不要崇拜他们
- 给事物以适当名称
- 添加类型提示和 XML 文档
- 从调用器@'s 角度考虑 API 表面
- 处理您在“1”阶段发现的边缘病例
关键地说,,这是非-的最佳时机 技术利益攸关方反馈=. 该功能现已可见且可用@ , @ {但您已经有'} 还没有投入时间来为它进行写作测试 MS K2} 如果他们说“ "” 其实是 @ MSSK4} 我们想要做 X 而不是 “,"” 您可以在删除一个综合测试套件时不心碎地跳过
阶段3:锁定它
咒语: 让它具有弹性 .
Now — @ @ @ and only now @—}你才寫下你的測試.#新增安全檢查@. 實施生产邊緣病例的正確錯誤處理Q.}新增監控與守護列 *.}
为什么等到现在 你终于知道自己在测试什么.
在“1”阶段中写作的测试本会是错误的 @—}你没有理解问题 但是在 “2” 阶段里写的._Tests 正确@,#因为执行稳定 #.#
[Theory]
[InlineData(0)]
[InlineData(1)]
[InlineData(100)]
public async Task ProcessItemsAsync_WithValidRequest_ReturnsCorrectCount(
int itemCount)
{
// Arrange
var items = _fixture.CreateMany<Item>(itemCount).ToList();
_mockRepository
.Setup(r => r.GetActiveItemsAsync(It.IsAny<Guid>(), It.IsAny<CancellationToken>()))
.ReturnsAsync(items);
// Act
var result = await _sut.ProcessItemsAsync(
new ProcessingRequest { AccountId = Guid.NewGuid() });
// Assert
result.TotalCount.Should().Be(itemCount);
result.Items.Should().HaveCount(itemCount);
}
[Fact]
public async Task ProcessItemsAsync_WithNullRequest_ThrowsArgumentNullException()
{
// Act & Assert
await _sut.Invoking(s => s.ProcessItemsAsync(null!))
.Should().ThrowAsync<ArgumentNullException>();
}
[Fact]
public async Task ProcessItemsAsync_ExcludesDeletedItems()
{
// Arrange
var items = new[]
{
new Item { Status = ItemStatus.Active },
new Item { Status = ItemStatus.Deleted },
new Item { Status = ItemStatus.Active }
};
_mockRepository
.Setup(r => r.GetActiveItemsAsync(It.IsAny<Guid>(), It.IsAny<CancellationToken>()))
.ReturnsAsync(items);
// Act
var result = await _sut.ProcessItemsAsync(
new ProcessingRequest { AccountId = Guid.NewGuid() });
// Assert
result.Items.Should().HaveCount(2);
}
这一阶段还包括:
- 安全审查: @(@auditation_,}授权,}输入验证=)}
- 组合货币因素 @(}在负载下发生的一切*?)
- 失败模式分析@( @ what if the database is slow #?}如果 API 乘以% ?)#
- 监控我们如何知道这种生产中断?
反馈编程
不同的人应该参与到不同阶段... ..."."你搞错了,你会沉溺在摩擦中
graph TB
subgraph "Phase 1: Technical Voices"
P1[Make It Work] --> T1[Senior Dev Review]
T1 --> T2["Does the approach<br/>make sense?"]
T2 --> T3["Any obvious<br/>architectural issues?"]
end
subgraph "Phase 2: Non-Technical Voices"
P2[Make It Pretty] --> N1[Product Owner Demo]
N1 --> N2["Does it meet<br/>the requirement?"]
N2 --> N3["Is the UX<br/>intuitive?"]
end
subgraph "Phase 3: Ops/Security Voices"
P3[Lock It Down] --> O1[Security Review]
O1 --> O2[Operations Review]
O2 --> O3["Will it hold up<br/>in production?"]
end
T3 --> P2
N3 --> P3
style T1 stroke:#3b82f6,stroke-width:2px
style N1 stroke:#8b5cf6,stroke-width:2px
style O1 stroke:#ef4444,stroke-width:2px
阶段1: 科技界的利益攸关方会因粗略代码而惊慌并询问按钮颜色,
阶段2: 非- 技术声音欢迎@. @% 此功能可用@ , @ buttons do things @ MS K3} 让产品所有者和设计者参与进来吧$—#changes are still cheap *,}你已经拥有了'_ t书面测试@.}
阶段3: 若有人通过恶意输入 ?" 仅当功能真正有效时才重要 @.
作为沙箱的分支
这里的' 改变了我的发展生活 直到你升起一个PR,, 你的分行是完全私人的.
但我看到开发商会处理每件事情 就像它一样... '...
我要你将你的特色分支 当作一个沙箱. 一个操场•. 一个实验室 那里预计会发生爆炸 没有人会受到伤害
graph TD
A[Feature Branch Created] --> B[Experiment Freely]
B --> C{Did it work?}
C -->|No| D[Try Something Else]
D --> B
C -->|Yes| E[Clean Up the Mess]
E --> F[Interactive Rebase]
F --> G[Squash to Clean Commits]
G --> H[Raise PR]
H --> I[Public Code Review]
style B stroke:#f59e0b,stroke-width:3px
style D stroke:#f59e0b,stroke-width:2px
style H stroke:#10b981,stroke-width:3px
subgraph "Private - Go Mental"
B
C
D
E
end
subgraph "Public - Be Professional"
H
I
end
在实践中,这代表着:
- 经常提交@ , @ 即使代码是'_ t编译的
- 写入
FIXME和TODO各地的评论 - 您正在探索时请保留调试代码@ ' @ label
- 拥有多个@ "_ 尝试此方法 @ MS K1\ 承诺
- @ '_ 不要担心发送信件@ MS K1_you'_ll global later @)}
在你升起PR:之前
- 清理代码“(”阶段 “2)”
- 添加测试@( @ 阶段 @ 3)}
- 交互反弹以粉碎所有混乱的历史
- 写入正确的承诺信件
PR审查员看到的是干净的, 专业代码,并有明确的叙事性. 他们没有看到6个错误开头吗?
沙箱保持私人状态 . 抛光工作公之于众
为何如此减轻压力
传统"在任何时候都是专业的 "发展正在粉碎." 每条线都感觉永久"MS K3" 每个决定都会感到后果. 通过抑制混乱来消除这种压力 游戏是合法工作 在“1,”阶段中,当轴线在“2,”阶段是廉价的时会收到反馈,测试一旦正确写入了 “3” 阶段,因为你终于知道什么是你''re testation".}
"Play是合法的工作
当域名被很好地理解或您're 锁定一个已知的错误时, TDD工作非常出色。 但是当问题仍然不明朗的时候, ,写测试通常意味着在行中三次检测错误的东西。
柔软的固态(和DRY'"肮脏的秘密)"
所以这三种“-”阶段的思维模式 对你的设计原理有什么影响呢? “?” 在简而言之, 它杀死了狗狗. 这里它如何改变我对 SOLID’, DRY,}和CQ#.中的界面的想法
我提到在“2.”阶段应用了SOLID原则,
SOLID是一套好的原则 我教他们.我使用它们 但I'曾见过球队 以"单一责任"或 Open/的名号 从抽象兔子洞中消失
在这里,' ~这是我的奢侈 ~: ~
当“:” 时应用 SOLID
- 您有证据您需要灵活度
- 抽象使代码更加清晰
- 您正在建造其它用的东西
Doná't 使用 SOLID 当 @ :
- 你怀疑未来的需求
- 抽象性增加了复杂性,而没有清晰
- 您正在建立只有您维持的内部代码
三个相似的代码线比一个不成熟的抽象线更好@ .} 当您看到这个模式清晰可见时, 您总是可以稍后提取 .\ _ 您可以 ' 无法轻易地解开在您的架构中烤熟了的抽象元素“ MS K3”\ Name.
// Over-engineered SOLID worship
public interface IThingProcessor { }
public interface IThingProcessorFactory { }
public class ThingProcessorFactory : IThingProcessorFactory { }
public class ThingProcessor : IThingProcessor { }
public class ThingProcessorDecorator : IThingProcessor { }
public class ThingProcessorValidationDecorator : IThingProcessor { }
// What you probably actually need
public class ThingProcessor
{
public async Task<Result> ProcessAsync(Thing thing)
{
// Just do the thing
}
}
如果您需要灵活性, 稍后@ , @ 重新设置是便宜的 @ MS K1+% Over-# 工程设计前方成本昂贵, 因为您在保留抽象层( 正在维持) '} # 没有赚钱来保持@ I. @ *
稻草陷阱
当我们屠杀神圣的牛群时 让我们谈论 试 “(Don'不重复你自己). ItMS K3}或许是我们行业中最教条性应用的原则 ‘,’当不加思索地运用它时, 它造成的伤害比好的还大。”
问题是这个: 并非所有重复工作都是同样的重复工作.
当两种代码看起来相似,但用途不同时 , 将它们带入一个共同的抽象夫妇中 应该独立进化的东西
- 添加处理这两个案件的参数和条件
- 打破另一个使用大小写
- 将共享代码复制出来并编辑@(_Admitting DRY是错误的 @)}这是错的
// "DRY" solution - looks clever, causes pain
public async Task<Result> ProcessEntity<T>(
T entity,
bool validateFirst = true,
bool sendNotification = false,
Func<T, bool>? customFilter = null,
Action<T>? preProcess = null,
Action<T>? postProcess = null) where T : IEntity
{
// 50 lines of conditional spaghetti trying to handle
// "processing" for Users, Orders, and Products
// because they all had 3 similar lines once
}
// What you probably actually need
public async Task<Result> ProcessUser(User user) { /* 15 clear lines */ }
public async Task<Result> ProcessOrder(Order order) { /* 15 clear lines */ }
public async Task<Result> ProcessProduct(Product product) { /* 15 clear lines */ }
是的.}但每种方法都是:
- 孤立地容易理解
- 容易修改,无副作用
- 当该实体类型消失时容易删除
- 容易用明确的投入和产出进行测试
DRY 版本是一个要维持的噩梦, 因为它“'”试图成为所有呼叫者的所有事物。 “. ” 每一个变化都需要理解所有的使用案例 ‘. ’ 每个错误修正都有可能打破其它东西 ’. }
我的规则 : 保持重复,直到你看到 相同 你相信它代表着 相同 将会一起进化的理念 到那时为止 , 来点拷贝 MS K2\paste是好的 它不是道德上的失败
重复比错误的抽象更便宜@ . @ 您可以稍后在模式清晰时进行复制 .} 您可以 MS K2 不能轻易地解开一个错误的摘要, 它通过您的代码库编织了\ I'%s
C# 中的界面问题
这是又一头需要宰杀的圣母牛 每个班级都需要一个接口\ "}模式.
在“.NET 依赖性注射”的早期, 有人决定要制作代码测试@,}您需要在每个地方输入界面。逻辑是 @: 您可以模拟一个混凝土类,}所以每个服务都需要 IService 和 ServiceImpl@. 忽然每个 C# 代码库都堆满了纯用于测试@.}的单一#-实施接口
// The interface tax - early 2010s style
public interface IUserService { }
public interface IOrderService { }
public interface IEmailService { }
public interface INotificationService { }
public interface IPaymentProcessor { }
public interface IInventoryManager { }
// Each with exactly ONE implementation
public class UserService : IUserService { }
public class OrderService : IOrderService { }
// ... you get the idea
这是货物邪教编程... .... 我们为此缴纳了复杂税 理论理论 可检验性和可转让性 理论理论 很少实现的未来灵活性 @.
在这里,'的东西\ :}你不需要这个了?
现代模拟框架等 替代物 和 摩卡 以虚拟方法模拟混凝土类@(}更重要的是#,}ASPQ.NET Core{'}DI容器使用混凝固型態做得很好
// Modern approach - just register the concrete type
services.AddScoped<UserService>();
services.AddScoped<OrderService>();
services.AddScoped<EmailService>();
// In your controller or service
public class OrderController(OrderService orderService, UserService userService)
{
// Primary constructor injection - clean and simple
}
无接口@. @ 否 IOrderService{\fn黑体\fs22\bord1\shad0\3aHBE\4aH00\fscx67\fscy66\2cHFFFFFF\3cH808080}你需要什么服务?
"但是测试呢??" 我听到你哭泣的叫声
单位测试@, 您有选项 @:
- 制作密钥方法
virtual使用模拟框架 - 使用测试数据库的实际服务@(集成检测通常更有价值,反之亦然 @)#
- 添加接口 当您实际需要测试时
第三点是关键 重构工具在提取接口方面特别好.
在骑士或视觉演播室 , * It'* *字面上 Ctrl+. →{"_Extractime}" 和您MS K3re}. 每套方法都得到解析 , 班级得到更新 以实施它MSKO6 你能找到/ 替换所有需要的用法
// Phase 1 & 2: Just write the class
public class OrderService
{
public async Task<Order> CreateOrderAsync(CreateOrderRequest request) { ... }
public async Task<Order?> GetOrderAsync(int orderId) { ... }
public async Task CancelOrderAsync(int orderId) { ... }
}
// Phase 3: Need to mock it for testing? Extract interface in 2 seconds
public interface IOrderService
{
Task<Order> CreateOrderAsync(CreateOrderRequest request);
Task<Order?> GetOrderAsync(int orderId);
Task CancelOrderAsync(int orderId);
}
public class OrderService : IOrderService { ... }
我的工作流程现在: *介面在 3,}不是阶段 1.
在“"”期间, 让它正常工作。 “"”和“"”使它漂亮一点。 我只是写了几堂课。“.”没有过早的抽象性;“MS K5” 没有对每种类型征收界面税。” IFoo 和 Foo@),和更快写@.#
当我打"的时候 把它锁住 " 然后需要写测试报告 时当时 我决定哪些服务实际需要嘲笑@. @ 通常需要它='}少于您 @'d think_ —}通常只是外部整合, 如 HTTP 客戶端@MS K4}数据库@,和信件队列@MSSK6{
对于内部商业逻辑服务来说 ? 一半时间我只是直接用真正的依赖性测试它们 思考 行为应该为“.”
// Services I typically DO extract interfaces for (external boundaries)
public interface IPaymentGateway { } // Third-party API
public interface IEmailSender { } // External service
public interface IBlobStorage { } // Cloud storage
// Services I typically DON'T need interfaces for (internal logic)
public class OrderValidator { } // Pure logic, test directly
public class PriceCalculator { } // Pure logic, test directly
public class OrderService { } // Test with real repo + in-memory DB
要点是: 停止前端的“"”接口@ just in case @". 写入混凝土类\ . 如果您需要测试或真正的多形态学界面, 稍后@,}用现代工具抽取一秒
您的代码库将更简单@ , @ 更多的导航@ I, 并赢得了您的维护重任, 让接口与执行同步, 没有好处@ MPK3}
Agile 连接
具有讽刺意味的“,”这个方法更符合 原原件 @(Q 阶段@1).Q 响应变化:#(-阶段 @1-2).Q客户协作, 当功能明显可见时, 请与客户合作 {(_ 阶段+2).}没有指纹点,} 没有速度追踪>,* 没有血腥的烧焦图表 *.
现代的“" @Agile"}”被进程爱好者所捕捉,他们加了如此多的仪式。 “'_没有时间进行探索”,这让大家回想起“MS K5{不是作为一种宽容,而是作为一个创造性工作的合法阶段 ‘.’
当不使用此方法时
我应该诚实地说 这是' 总是正确的做法
错误修正@: @% 当您@ '_ 正在修补已知的bug@ MPK1} TDD 风格更有意义 @ MS K2} 写一个测试来复制 bug@ ,} 修正它\ , done @ OMK5}
好了,好了。 明白了问题。 如果您正在实施标准算法或模式, 您已经使用过百倍的 ,您不需要一个勘探阶段@.}
有已知要求的紧凑期限@: @ 如果您真的知道您正在建造什么 're 以及何时需要发运什么 ,, 您可能会跳过“ MS K2 探索” 的阶段@.
高度受监管的环境 @: 如果每个承诺需要签名“-@off @,}沙箱更难 #.}尽管您仍然可以在推前在本地进行勘探_.}
但对于 多数 要求有点模糊的, 技术方法是 ' t显而易见\ I,\ 并且你需要空间去思考\ -—\ 这种方法非常管用
给初级开发者的说明
如果您在职业生涯的早期 '@,}你可能会感到压力 要显示""\perfect @"}代码 您必须使用这三种“.”来表示 “-_Phase Projession” MSk7}—}只是要弄清楚你在“MS K9>re in,’中是哪个阶段,并且总是完成整个工作直到“MSC11”阶段 如果导致在末端“MSG15”上擦亮了‘,’测试了“MSHK13 生产<-{\resident 代码”) 没人会责怪您乱糟蹋勘探代码
底线
让它运作起来 使它漂亮点 关上门
启动简单@ . @ 设计前瞻而非猜测@ I. @ Iterate 快速@ O.} 只给钱时添加复杂性@ MPK3 @
@"@Dexplication 远比错误的抽象更便宜 #."#
探索阶段是' 一个有罪的秘密 '—iit ''s a confident part of the process '."你的分支就是你的沙箱 直到你提出一个 P."技术反馈
这保持了创造力的活力 , 减少了压力 MS K1 并产生了规模优雅的系统
停止尝试从第1分钟开始完美到完美的状态 @ . @ 允许自己播放@ MS K1} @
相关阅读
若此回响响应@,, 您可能也会享受这些相关文章 @:#
- 我如何构建软件 “-”更深入地探讨这一方法背后的测试理念“,”,并附有三个阶段的详细代码示例:“.”
- 每天的起立都是胡说八道 除非他们赚到薪水 以及如何评估你们的敏捷仪式是否在赢得他们的保护?
- 拼图拼图@: @ 写入实际效果的特征描述 @-_# 因为这些图纸是工具@,}不是圣经 —}作为活文件对待它们 是构建正确事物的唯一方法
- 遗产法典库工作 包括如何运用这些原则, 当你学习了一种你曾经建立过的系统时... ...'_t building...