游戏服务器开发经验(五)应对复杂需求
什么是复杂需求? 在本文中,“复杂需求”指的是, 需求涉及到非常多的模块; 每个模块都有多层抽象重载; 状态变更逻辑复杂,边界情况众多,策划无法给出统一处理的方案; 要在已有的功能代码上进行开发,避免造轮子。需要在满足新需求的同时,保证之前的功能正常。 或者更简单的判断方式是,你看着代码思考的时候,发现脑容量上下文无法处理所有可预料的情况时,就是复杂需求。 为什么我们往往分不清复杂需求? 由于需求本身足够复杂,策划也是人,AI也有上下文限制,大家都无法将需求一次性分析完整 因此策划说的话 和 写的内容,可能是寥寥数语。因为很多细节策划在自己没有玩游戏,并且自己没看代码的情况下,确实无法思考具体的需求细节;策划那边也会每天思考,脑暴,过了一段时间又有新的想法。 程序这边收到的需求,可能只是完整需求的冰山一角。 直到需求慢慢地补全,在主玩法周围慢慢补全其他的需求的时候,才会发现这个需求的规模。 也就是说,当需求大到“脑容量上下文无法处理所有可预料的情况时”,我们就要有警报在脑袋里开始轰鸣,是复杂需求来了。 为什么复杂需求难以开发? 横跨多个模块,一个人是无法同时了解多个模块的业务逻辑的,一个模块之前的业务逻辑不一定适配当前的新业务,需要仔细评估是复用还是造轮子; 策划的需求可能只有一个预期的方向,但是这个方向上的边界情况可能非常复杂,需要程序来整理可能的边界,并且要做出取舍; 策划提出的需求可能是一个很特殊的方案,但是在大型项目中可能有不同的实现方式。如果贸然按照策划的思路实现,可能会带来副作用。因此需要耐心研读已有代码,领会之前的代码结构设计。 实现需求并不是终点,实现需求并提供一种可复用的机制,才是开发的正确标准。一旦过早“为了实现而实现”,会导致代码非常僵硬,并且与框架层代码脱节而难以处理各种情况。 如何应对复杂需求 在需求没有完全出来的时候,可以写一些“为了展示效果而临时开发”的代码,帮助策划在游戏内体验,寻找设计方向;但是这些代码自己心里一定要有数,只是临时实现。后续要争取时间进行优化; 需求慢慢完备之后,需要花时间站在更高的角度上分析需求涉及到的模块,分析需求中的主线脉络,对应于代码里的设计模式。而不是头痛医头,脚痛医脚,把大需求拆成小需求后,针对各个小需求自己实现自己的部分,到最后无法统一管理; 如果能够站在更高的角度上切分需求和涉及到的模块,就能针对各个模块去了解用到的部分的代码。我们并不需要完整领悟别的模块的代码,但是自己需求里用到的部分一定要心里有数 如果我们无法正确切分需求,则需要寻找需求涉及到的模块的负责人,询问他们相关需求的实现是什么样的。这里要记得,一定要说出完整的需求链路,而非单纯询问某个API。一定要让其他模块的负责人知道合适的上下文,让他们的判断来帮助我们更好的正确实现需求。 如果发现有一块代码怎么写都非常别扭,需要处理各种边界的时候,说明代码里有一部分逻辑是可以整理的。 如果需求真的很复杂,如果已经走了弯路,如果已经到了身心俱疲的情况,请务必向上反馈,争取时间。精神burnout带来的伤害是客观存在的,要好好保护自己的身心健康。 打好每一行日志。我们无法理解所有已有模块,但是我们可以靠日志了解运行的时序。 记得自测。一定要自测。哪怕是用GM指令自己构建一个环境也要自测。在复杂需求中,只有日志和游戏里的自测能够让我们确认自己的思路是否正确。 在策划自己也拿不准情况的时候,很多边界,如果不会带来严重后果,不用着急去问策划,而是可以自己先记下来。当功能开发差不多了,可以在游戏里体验时,再告诉策划,让策划边体验边思考这些边界应该如何处理。 如何使用AI帮忙 让AI帮忙分析你的需求。请记住,这里一定要把客观的需求告诉AI,而非把自己的方案告诉AI。在过去的很长一段时间里,AI无法完整领会需求,AI写的代码无法编译通过等。但是现在,opus4.6已经能够客观冷静分析需求。AI能翻阅整个项目的代码,去搜索其中它感兴趣的代码,它可以在代码仓库的角落里找到很多有用的功能,也会分析已有项目的设计模式。大多数情况下,它做的比人类更好。 让AI与你进行头脑风暴。AI不知道的是你的需求具体要实现什么。即使已将需求文档提交给它,也会产生误解。另外,它偶尔会选择“看起来最合适的方案”。这个时候就要与它进行多轮的头脑风暴,告诉它我们具体的需求和期望。 让AI帮你整理你不懂的代码模块。很多设计模式可能会将代码写的很松散,继承好几层,导致代码很难整体阅读。让AI整理你感兴趣的代码和已有的需求是如何实现的,可以加速我们对需求的理解。 应对复杂需求时的心态 如果需求已经复杂到你无法一次性想通所有场景和边界时,说明这个需求一定是多个模块互相组合的。在这种情况下,最大的敌人是完美主义,是希望“一次全做对”的想法。 不要害怕返工,不要觉得自己写的东西被删了是浪费时间。相反,正是因为有了这些歪路,才让我们找到了正确的方向。真正需要恪守的规则是,我们最后提交的方案一定要是我们能做到的最好的方案。 考虑不到那些需求的边界是很正常的事。不要因为没考虑到而自责,而是把它当做一件习以为常的事,就像家里的猫咪又去扒拉沙发,不小心把饭碗打翻一样,无法避免。 不要害怕bug很多的情况。当然,前提是你已经了解需求的主要脉络,在代码里做好了兜底处理,尽可能保证不要内存泄漏,要是真的崩了只崩自己的这个需求。bug多并不直接意味着人不行,也有可能是需求确实复杂,涉及模块确实众多,很多边界来不及考虑。 接受“人脑无法处理所有情况”的现实。如果你要思考需求,请你打开代码,对着已有代码进行思考。不要在没有代码的时候思考需求如何实现。 未雨绸缪 每一个正在服役的项目框架,一定有自己特有的兜底机制。在实现需求的时候,也许他们要的很急,你也应该多想想能否用得上那些兜底机制。 框架里一定有一些非常常用的模块,那些模块一定要多了解,多看代码。特别注意:放下评价代码的标尺,切换为“它这么写是为了解决什么?”“我能用这块逻辑吗?”的思考方式。验证方式是下一次看到某个已看过的模块的时候,在脑海里能回忆起粗略的细节,而非“它写的还可以”“我看不懂,好累啊” 在需求刚宣讲的那天,一般会有一小段时间,你既知道策划想要什么,又知道大概的模块有哪些,而且此时还没有定具体的实现细节。这个时候就是最佳的分析已有模块的时机。千万不要错过这个窗口期。 你的工作不等于你的价值 复杂需求让人最难过的一点是,随着需求会一点一点的明确,之前写的代码会不停地被删掉,仿佛他们之前不应该出现,与此同时,随着需求的清晰,需求的边界也慢慢清晰,我们会慢慢发现之前的思考都是错误的。 谁喜欢犯错呢?谁都不喜欢犯错。但是复杂需求就是这样,有一种让所有人不得不犯错的能力。即使你知道项目里各个模块的业务,你也很难一次两次就把功能做完善。更有可能的是,策划体验完之后,过来说,“我们对刷新/每次/减半/随机的理解好像不太一样” 如果你之前写了太多简单的增删改查、抽奖发奖,做的技能都是具体的某段逻辑,不需要考虑那些边界的话,可能很难想象一件事不断地挂在胸口,仔细一思考全是边界,看着需求截止日不断来临,而代码里还有很多临时实现。那一种绝望的感觉。 但是,这种绝望并非我们程序一人所致。要慢慢接受这种“胸口挂着事情”的感觉,接受“需求复杂到无法光靠大脑想明白”,而是要打开代码和AI大模型进行分析;在需求截止日之前提前预警,如实描述当前遇到的问题,需要什么样的帮助。最后,工作只是工作。 你的工作不等于你的价值。 ——《伯恩斯新情绪疗法》 我最近总有一种感觉,我坐在电脑前,看着显示器里的代码。我感觉我的眼睛和显示器之间,似乎有一堵无形的空气墙,或者是透明保护罩。我开始看很多项目里常用的底层框架和模块,静下心来思考当时是为了解决什么问题。 承认自己的渺小与无知 我曾以为大厂的程序员一定上知天文下通地理。但最让我惊艳的是他们的思考方式。我总是过早的希望得到一个结论,然后去实施。但是在大厂程序员和AI大模型眼里,需求是需要一遍遍确认的;已有代码是需要认真研读的;边界是肯定想不全的,但是要提前想;已有的代码是要复用的。他们很多决策都是依赖已有代码,他们是看着代码进行分析的。他们也许记不住某个具体函数名,一次性无法完全在心里想全所有业务边界,但却可以巧妙的通过合理的代码设计来管理热点内存数据,保证内存不泄露,正确申请、使用和释放。 我们本来就不可能理解所有模块的代码,就算是AI大模型也有上下文的限制。但是我们可以通过其他模块的配合,和自己的模块的干净维护,来保证需求的健壮,在不同的边界上执行已有的处理方式。(至少不会crash) 渺小和无知是我们人类的常态,渺小和无知并不可耻,可耻的是知道自己无知而放弃了学习,可耻的是不愿意选择正确的方式继续推进开发。
