熟悉大型代码库的最佳方法是什么?

加入一个已经拥有大型代码库的现有团队可能会让人望而却步

  • 尝试从代码中获得一个关于所有东西如何连接在一起的总体概述
  • 一次只关注一小部分代码,理解它们是如何完全工作的
  • 选择一个特性进行开发并在开发过程中学习
  • 尝试从类图和 uml 中获得洞察力,如果可用的话(并且是最新的)
  • 还有别的事吗?

我正在做一个大约2万行的 C + + 应用程序和库(编辑: 在宏伟的计划中很小!).在工业界,我想你会得到一位经验丰富的程序员的介绍。然而,如果情况并非如此,您可以做些什么来尽快开始增加价值?

--
答案摘要:

  • 单步执行调试模式下的代码,以查看它是如何工作的
  • 与比你更熟悉代码库的人配对,轮流扮演编码者和观察/讨论者的角色。在团队成员中轮换合作伙伴,这样知识就可以传播开来。
  • 编写单元测试。首先断言您认为代码将如何工作。如果结果与您预期的一样,那么您可能已经理解了代码。如果没有,你有一个难题要解决,或者有一个调查要做。(谢谢多纳尔,这是一个伟大的答案)
  • 以与上面类似的方式遍历现有的函数代码单元测试
  • 通过阅读 UML、 Dox 生成的类图和其他文档,可以对代码有一个广泛的了解。
  • 进行小的编辑或错误修复,然后逐步建立
  • 记下笔记,不要急于开始开发; 花时间去理解比生成混乱或不适当的代码更有价值。

这篇文章是 使自己熟悉继承代码库的最佳方法的部分副本

14309 次浏览

如果可能的话,从一些小任务开始,围绕您的问题调试代码。 在调试模式下逐步通过代码是学习某些东西如何工作的最简单方法。

我觉得你需要把这个和特定的任务联系起来。当你有时间的时候,选择你想要的方法。

当你有一些事情需要去做的时候,给自己一个狭窄的焦点去完成它。

这完全取决于你是什么样的学习者和什么样的程序员,但是:

  • 广泛的第一-你需要一个范围和大小的想法。这可能包括浏览文档/uml,如果它们是好的。如果这是一个长期的项目,你需要一个完整的理解一切,我实际上可能会正确地阅读文件。再说一次,如果他们很好。
  • 缩小范围,挑选一些易于管理的东西,并试着去理解它。
  • 选择一个功能——如果你感到自信的话,可以选择一个与你刚才看到的不同的功能,然后开始做一些小的改变。
  • 迭代——评估事情进展得有多顺利,看看是否可以从更深入地重复早期步骤中获益。

我建议在其上运行 Doxy,以获得最新的类图,然后进行一段时间的扩展。这将为您提供一个快速的大图片,您可以在近距离接触代码时使用它。

另一种选择是为您感兴趣的特性编写测试。设置测试工具是确定系统具有哪些依赖项以及其状态位于何处的好方法。每个测试都以一个关于您认为系统应该如何工作的断言开始。如果结果是这样的,那么您已经取得了一些成就,并且您已经得到了一些可用的示例代码来重现它。如果不是这样的话,你还有一个难题要解决,还有一系列的问题要解决。

与另一个更熟悉系统的程序员一起开发一个新特性或者修复一个 bug。这是我见过的最好的方法。

我同意这完全取决于你是什么类型的学习者。话虽如此,我曾经在两家公司工作过,这两家公司的代码库都非常庞大。通常情况下,我是这样工作的:

如果可能的话,在查看任何函数代码之前,我先看一下已经编写好的单元测试。这些通常可以帮助相当多。如果没有,我会做以下事情。

首先,我在很大程度上忽略了实现,只关注头文件,或者只关注类接口。我试图了解每堂课的目的是什么。其次,我从一个最重要的领域开始,深入到实现的一个层次。这很难衡量,所以偶尔我只是从顶部开始,然后在文件列表中一路向下。我称之为广度优先学习。在完成这个初始步骤之后,我通常会对代码的其余部分进行深度分析。最初的广度优先外观有助于巩固/修复我从接口层面得到的任何想法,然后深度方面的外观向我展示了用于实现系统的模式,以及不同的设计想法。通过深度优先,我的意思是您基本上使用调试器逐步通过程序,逐步进入每个函数以查看它是如何工作的,等等。对于真正的大型系统来说,这显然是不可能的,但是20kLOC 并不是那么多。:)

首先,如果您的团队成员有使用代码的经验,您应该安排他们与您一起对代码进行概述。每个团队成员都应该向你提供他们专业领域的信息。让多个人来解释事情通常是有价值的,因为有些人会比其他人解释得更好,有些人会比其他人有更好的理解力。

然后,你需要在没有任何压力的情况下开始阅读代码一段时间(如果你的老板愿意的话,几天或者一个星期)。它通常有助于自己编译/构建项目,并能够在调试模式下运行项目,这样您就可以单步执行代码。然后,开始弄湿你的脚,修复一些小错误,做一些小的改进。您将有望很快准备好一个中等规模的项目,以及稍后的一个大型项目。继续依靠你的队友,因为你去-往往你可以找到一个特别愿意指导你。

如果你挣扎,不要对自己太苛刻——这很正常。理解一个庞大的代码库可能需要很长时间,可能需要几年。实际上,即使经过多年,代码的某些部分仍然有点令人恐惧和不透明,这种情况经常发生。当你在项目之间停工的时候,你可以深入挖掘这些领域,你会经常发现,经过几次尝试之后,你甚至可以弄清楚这些部分。

祝你好运!

我发现直接跳到代码中可能有点难以承受。尽可能多地阅读有关设计的文档。这有望解释每个组件的用途和结构。如果一个现有的开发人员可以带您通过它,那是最好的,但是这并不总是可能的。

一旦您熟悉了代码的高级结构,就可以尝试修复一两个 bug。这将帮助您掌握实际的代码。

让团队安排你进行两周的 bug 修复(如果你有两周的时间的话)。他们会很高兴有人为此负责,到这个阶段结束的时候,你将花费大量的时间与图书馆解决问题,你可能会知道它非常好。

严格旋转配对。

如果可能的话,在浏览文档/代码库时,尝试使用严格轮换的配对。也就是说,你们两个人坐在一起一段固定的时间(比如两个小时) ,然后你们交换位置,一个人继续做那个任务,另一个人和另一个人一起做另一个任务。

两人一组,你们都会学到一些知识,当轮换发生时,这些知识可以提供给团队的其他成员。这样做还有一个好处,就是当一个新的组合出现时,参与任务的人(在本例中是研究代码的人)可以用一种更容易理解的方式来总结和解释概念。随着时间的推移,每个人都应该有相似的理解水平,并希望避免“哦,只有约翰知道那一点代码”综合症。

从我可以告诉你的场景,你有一个很好的数字这(3对) ,但是,如果你是分布式的,或不工作在相同的时间尺度,这是不太可能的。

如果它有单元测试(我打赌它没有)。从小处着手,确保单元测试不会失败。如果你立刻盯着整个代码库,你的眼睛会变得呆滞,你会感到不知所措。

如果没有单元测试,则需要将重点放在所需的特性上。运行应用程序,查看应该影响的功能的结果。然后开始查看代码,试图找出应用程序如何创建您想要更改的内容。最后更改它,并检查结果是否按照您希望的方式显示。

你提到它是一个应用程序和一个库。首先更改应用程序,并坚持使用库作为用户。那么在你学习了图书馆之后,就会更容易改变。

从自顶向下的方式来看,应用程序可能有一个主循环或主 GUI 来控制所有的动作。值得了解应用程序的主要控制流程。这些代码值得一读,让你自己对应用程序的主要流程有一个大致的了解。如果它是一个 GUI 应用程序,创建一个文件,显示哪些屏幕有以及如何从一个屏幕到另一个屏幕。如果它是一个命令行应用程序,处理是如何完成的。

即使在企业中,采取这种做法也并不罕见。通常没有人完全理解应用程序是如何工作的。人们也没时间带你到处转转。他们更喜欢关于具体事情的具体问题,所以你必须自己挖掘和实验。然后,一旦您得到了您的具体问题,您就可以尝试为应用程序的这一部分隔离知识源,并询问它。

我通常会建议那些尚未被提及的人们,在成为开发人员之前,成为现有代码库的合格用户是非常重要的。当新的开发人员加入到我们的大型软件项目中时,我建议他们在努力编写代码之前先花些时间成为专业用户。

也许这是显而易见的,但我看到很多人试图太快地跳入代码,因为他们渴望开始取得进展。

你可以考虑看看 源代码逆向工程工具,我知道有两种工具:

这两个工具提供了类似的功能集,其中包括静态分析,生成软件中模块之间关系的图表。

这主要由调用图和类型/类的优化组成。查看这些信息应该可以很好地了解代码的各个部分之间是如何相互关联的。使用这些信息,您可以深入挖掘您最感兴趣的部分的实际源代码,并且您需要首先理解/修改它们。

从理解“问题域”开始(它是一个工资系统吗?)?存货清单?实时控制之类的)。如果您不理解用户使用的术语,那么您将永远无法理解代码。

然后看看对象模型; 可能已经有了一个图,或者您可能不得不对其进行反向工程(手动或使用 Doug 建议的工具)。在这个阶段,您还可以研究数据库(如果有的话) ,是否应该遵循对象模型,但可能不是,这一点非常重要。

查看一下更改历史记录或 bug 数据库,如果有一个区域经常出现,那么首先查看该区域。这并不意味着它写得很糟糕,而是每个人都在使用它。

最后,保留一些笔记(我更喜欢 wiki)。

  • 现有的家伙可以用它来理智地检查你的假设,并帮助你。
  • 稍后您将需要回过头来参考它。
  • 队里下一个新人会非常感谢你的。

我喜欢所有这样的答案,即您应该使用类似 Doxy之类的工具来获得类图,并首先尝试理解大图。我完全同意。

也就是说,这在很大程度上取决于代码一开始的分解情况。如果它是一个巨大的混乱,将很难学习。如果它干净,组织得当,应该不会那么糟糕。

(无耻的市场推广)

你应该看看 在线。它是一个用于导航和可视化大型代码库的 Eclipse 插件。我们的许多客户使用它来打印出主要流程的可视化,从而打入新的开发人员。

我也有过类似的情况,我会说你是这样的:

  • 如果它是一个数据库驱动的应用程序,那么从数据库开始,尝试理解每个表、它的字段以及它与其他表的关系。
  • 一旦可以使用底层存储,就向上移动到 ORM 层。这些表一定有某种代码表示形式。
  • 一旦完成,然后转移到如何和从哪里来的这些对象是来自。界面?什么界面?有确认吗?在它们进入数据存储之前,需要对它们进行哪些预处理?

这将使您更好地熟悉系统。请记住,只有当您非常清楚地知道 什么正在被测试,并且 为什么需要以这种方式在 只有中被测试时,尝试编写或理解单元测试才是可能的。

对于没有驱动到数据库的大型应用程序,我建议使用另一种方法:

  • 系统的主要目标是什么?
  • 那么,解决这个问题的系统的主要组成部分是什么呢?
  • 每个组件之间有哪些相互作用?制作一个描述组件依赖关系的图表。问问已经在做的人。这些组件之间必须相互交换一些内容,因此也要尝试弄清楚这些内容(比如 IO 可能会将 File 对象返回给 GUI 等)
  • 一旦适应了这一点,就可以深入研究其他组件中依赖性最小的组件。现在研究一下这个组件如何进一步划分为类以及它们如何相互作用。这样你就掌握了单个组件的技巧
  • 移到下一个最不相关的组件
  • 最后,转移到核心组件,这个核心组件通常依赖于您已经处理过的许多其他组件
  • 在查看核心组件时,您可能会回顾前面检查的组件,所以不要担心继续努力工作!

对于第一种策略: 以这个 stackoverflow 站点为例。检查数据存储,存储什么,如何存储,这些项在代码中有什么表示,如何在 UI 中显示它们。一旦它们返回到数据存储,它们从哪里来,以及在它们上面发生了什么处理。

第二个 以文字处理机为例。有什么组成部分?输入输出界面,页面等等。它们是如何相互作用的?随着你学习的深入,继续前进。

放轻松。书面代码是某人的心态,冻结的逻辑和思维风格,它需要时间来读取思想。

请参阅 这个答案,了解如何使用测试覆盖工具来定位感兴趣特性的代码,而不需要知道该特性在哪里,或者它是如何分布在许多模块中的。