如何最好地熟悉大型代码库?

81
加入一个已经存在大量代码的团队可能会让人感到不知所措。最好的方法是什么呢?
  • 广泛:尝试获得所有代码之间如何关联的整体概述
  • 狭窄:一次专注于小节,完全理解它们的工作原理
  • 选择要开发的功能,并在学习的过程中逐步实现
  • 如果有可用的类图和UML,请尝试从中获得洞察力(并保持更新)
  • 还有其他不同的方法吗?

我目前正在处理的是一个大约有20,000行C++应用程序和库(编辑:从大局来看很小!)。在工业界,我想您会由一位经验丰富的程序员进行介绍。但是,如果没有这种情况,有什么方法可以尽快开始添加价值呢?

--
答案摘要:

  • 通过调试模式逐步执行代码,查看其工作原理
  • 与比你更熟悉代码库的人搭档,轮流担任编码和观察/讨论的人。在团队成员之间轮换伙伴以传播知识。
  • 编写单元测试。从你认为代码将如何运行的断言开始。如果结果符合你的预期,你可能已经理解了代码。如果不是,你有一个需要解决的谜题或者需要进行查询。(感谢Donal,这是一个很好的答案)
  • 类似于上述方法,通过现有功能代码的单元测试进行学习
  • 阅读UML、Doxygen生成的类图和其他文档,以获得对代码的广泛感觉。
  • 进行小的编辑或修复错误,然后逐步建立起来
  • 做笔记,不要跳进去开始开发;花时间理解比生成混乱或不合适的代码更有价值。

本帖是the-best-way-to-familiarize-yourself-with-an-inherited-codebase的部分重复。


4
20K 行代码并不是非常庞大的代码库。 当它只有 20K 行时,我会阅读它。 我在大学里没有学到的一件事是如何处理大型代码库。 - Paco
确实,2万并不算太多。我们的C++文件每个文件都有超过1万行。我知道这很糟糕,但我们现在没有时间整理它。(想象一下我只是想到它就会翻白眼)但其中许多冗余代码来自于注释。 - HS.
2
哈哈,确实!我并不是想暗示20k是一个巨大的代码库(我从未这么说过),只是在寻找一般性、可扩展的建议。到目前为止,回答都很好,让我有很多思考的地方。 - RJFalconer
20k 是一个文件吗?;-) - user2864740
我曾经咨询过的一个地方有一个深度嵌套的if/then语句的40k行文件,实现了某种业务规则。这太糟糕了。 - Dave Newton
19个回答

29

如果可能,从一些小的任务开始,调试与你问题相关的代码。 在调试模式下逐步执行代码是学习如何运作的最简单的方法。


2
在调试时,考虑变量应该是什么,并且如果调试器显示不同,请找出原因。我个人不喜欢调试器,而更喜欢使用打印语句,这可以强迫你提前思考。 - extraneon
1
@extraneon 除了强制你提前思考,打印语句还可以让你更轻松地查看大量的输入。例如,如果一个变量通常为2,在100个循环中变为10,你可以通过printf语句来发现它,但是用调试器跟踪会更困难。 - Elazar Leibovich

18
另一个选择是为您感兴趣的功能编写测试。设置测试工具是建立系统依赖关系和状态所在位置的好方法。每个测试都以对您认为系统应该工作方式的断言开始。如果它确实按照这种方式工作,那么您已经取得了一些成就,并且有一些可用于重现它的工作示例代码。如果它不按照这种方式工作,那么您就有一个需要解决的难题和一个需要跟进的问题。

我一直认为这是熟悉别人代码的最佳方式。 - Bill the Lizard

13

我通常建议人们做的一件事情是,在成为开发者之前,成为现有代码库的能力强的用户非常重要。当新的开发者进入我们的大型软件项目时,我建议他们花时间成为专家用户,然后再尝试着去编写代码。

也许这很显而易见,但我已经看到很多人因为急于取得进展而试图太快地进入代码中。


9

这在很大程度上取决于您是哪种类型的学习者和程序员:

  • 宏观先行 - 您需要了解项目的范围和规模。如果文档/uml很好,这可能包括浏览它们。如果这是一个长期项目,并且您需要全面理解所有内容,我可能会认真阅读文档。再次强调,如果文档很好。
  • 逐步深入 - 选择一些可管理的内容并尝试理解它。对代码有个“品尝”体验。
  • 选择一个功能 - 如果您感到自信,可以选择另一个与您刚刚查看的不同的功能,并开始进行一些小改动。
  • 迭代 - 评估事情的进展情况,并查看是否可以从更深入地重复早期步骤中获益。

7

严格轮换配对。

如果可能的话,在阅读文档/代码库时,尝试采用严格轮换的方式进行配对。意思是,两个人一起工作一段固定的时间(比如说,2小时),然后交换搭档,一个人继续完成这个任务,而另一个则与另一个伙伴一起开始另一个任务。

在配对中,你们两个都会学到一些知识,然后在轮换发生时可以将这些知识传递给团队中的其他成员。好处在于,当新的搭档组合在一起时,那个曾经完成任务(在本例中是调查代码)的人可以总结和解释这些概念,使其更容易被理解。随着时间的推移,每个人应该都能达到相似的理解水平,从而避免“哦,只有约翰知道那部分代码”的情况。

根据我所了解的情况,你们有足够的人数来实现这种配对(3个搭档),但如果你们分散或者没有按同样的时间表工作,那么这种方法可能不可行。


6
我建议在代码上运行Doxygen,以获得最新的类图,然后进行广泛的研究。这将为您提供一个快速的大局观,您可以在接近和深入了解代码时使用它。

5

我认为这完全取决于你是什么类型的学习者。话虽如此,我曾在两家拥有非常庞大代码库的公司工作过。通常,我的工作方式如下:

如果可能,在查看任何功能代码之前,我会查看已编写的单元测试。这些通常可以帮助很多。如果它们不可用,则我执行以下操作。

首先,我基本上忽略实现,只查看头文件或类接口。我试图了解每个类的目的。其次,我从最重要的区域开始深入实施一级。这很难判断,因此有时我只是从顶部开始,按文件列表向下工作。我称之为广度优先学习。完成这一初始步骤后,我通常会深入实施以便更好地理解和巩固对接口层的理解,并了解实现系统所使用的模式以及不同的设计思路。通过深度优先,我指的是使用调试器逐步执行程序,进入每个函数以查看其工作原理等。当然,对于非常大的系统,这显然是不可能的,但20000行代码并不算太多。:)


3

与另一位更熟悉系统的程序员合作,开发新功能或修复错误。这是我见过最有效的方法。


3
我遇到过类似的情况,我建议你按照以下步骤进行:
  • 如果是数据库驱动的应用程序,从数据库开始入手,了解每个表及其字段,以及它们与其他表之间的关系。
  • 一旦了解了底层存储,就可以进入ORM层。这些表在代码中必须有某种表示形式。
  • 完成后,再查看这些对象来自何处。接口?哪个接口?有任何验证吗?它们在进入数据存储之前需要进行什么预处理?

这将使您更加熟悉系统。请记住,只有当您非常清楚地知道正在测试什么以及为什么需要以特定的方式进行测试时,才能尝试编写或理解单元测试。

对于不是面向数据库的大型应用程序,我建议采用另一种方法:

  • 系统的主要目标是什么?
  • 解决此问题的主要组件是什么?
  • 每个组件之间的相互作用是什么?创建一个描述组件依赖关系的图表。向已经在项目上工作的人询问。这些组件必须相互交换一些内容,因此请尝试找出这些内容(例如,IO可能会将文件对象返回给GUI等)。
  • 熟悉了这些后,深入研究最不依赖其他组件的组件。现在学习该组件如何进一步分成类,并且它们之间如何相互作用。这样,您就可以完全掌握一个单一组件。
  • 转到下一个最不依赖的组件
  • 最后,转到核心组件,通常会依赖于您已经处理过的许多其他组件
  • 在查看核心组件时,您可能会参考您早期检查过的组件,因此不要担心,继续努力!
对于第一种策略: 以Stack Overflow网站为例。检查数据存储,正在存储什么,如何存储,这些项在代码中有什么表示形式,以及它们在UI上的呈现方式。它们来自哪里,一旦它们返回数据存储,会进行什么处理。 对于第二种策略: 以文字处理器为例。有哪些组件?IO,UI,页面等。它们是如何相互作用的?随着您的学习逐步深入。
请放松。编写的代码是某个人的思维方式,冻结的逻辑和思考风格,需要时间来了解这种思维方式。

2

我认为你需要将这与特定任务联系起来。当你有充裕时间时,可以随意选择任何一种方法。

当你有需要完成的事情时,给自己一个明确的目标,集中精力去做。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接