在旧版代码库中,我如何快速找出正在使用的和未使用的?


21

我被要求评估似乎是一个实质性的旧代码库,作为签订维护该​​代码库合同的先驱。

这不是我第一次遇到这种情况。在目前的情况下,该代码适用于相当引人注目的和相当高负载的多玩家游戏站点,一次可支持至少数千名在线玩家。与许多此类站点一样,该站点是前端和后端技术的组合。

从内向外看,站点结构是一团糟。到处都有后缀为“ _OLD”和“ _DELETE”的文件夹。许多文件夹似乎毫无用处,或者具有非常神秘的名称。即使在看起来合法的文件夹中,也可能有任意数量的旧的,未使用的脚本摆在附近。不仅如此,即使在其他情况下运行的脚本中,无疑也有许多已失效的代码节(紧迫的问题要少得多)。

这是从现有维护者到站点的原始开发者/维护者的移交。可以理解,在这种情况下典型的情况是,任职者除了与合同和法律要求将移交给新当选的维护人员的合同和法律要求外,与移交无关。因此,从现有运营商那里提取有关现有站点结构的信息完全是不可能的。

进入代码库的唯一想到的方法是从站点根目录开始,然后慢慢地但一定要在链接的脚本中导航……这可能有数百种正在使用中,有数百种没有使用。鉴于该站点的很大一部分都位于Flash中,因此这甚至不那么简单,因为尤其是在较旧的Flash应用程序中,指向其他脚本的链接可以嵌入二进制文件(.FLA)中,而不是文本文件(.AS / ActionScript)中。

因此,我想知道是否有人对如何评估整个代码库的可维护性有更好的建议。如果有某种方法可以查看网络服务器操作系统(我可以访问)上文件访问频率的图表,那将是很棒的,因为这可能会提供一些洞察力,让您了解哪些文件最关键,即使不是能够删除那些从未使用过的文件(因为某些文件每年只能使用一次)。


7
我对Flash不太了解,但是如果在代码不存在的情况下出现编译错误,则应该能够重命名文件夹以查看是否引用了它们。
Oded 2012年

邪恶的解决方案:删除它们,然后等待错误/错误报告。(只需确保它可以恢复!)
Izkata 2012年

1
@Nick您能否澄清您是否仍需要继续竞标/否则获得的下一阶段合同的一部分要向您支付评估费用?您的答案不会改变“是否有工具”问题,但是我们中的一些人可以重新制定答案:更适合您的情况的过程(例如,避免陷入困境等)。
jcmeloni 2012年

@jcmeloni不,我没有得到评估薪水。但是根据我的经验,从最近几天我收到的小事情中,他们现在没有其他人在桌子上。我的技能非常不同寻常,因此根据报价,我更加放心,他们没有其他人争夺它。实际的报价是从我的未来客户到他们的客户,后者打算将合同重新授予他们。从我的目的出发,我实际上是要协助他们提供上述报价。HTH。
2012年

@Oded重命名绝对比反复试验删除更容易!好想法。这是包装盒中的另一种工具。
工程师

Answers:


32

由于您被要求做的是为您的客户提供输入,以便针对该代码的任何工作向其他客户(噩梦代码)编写适当的建议,因此我将继续并说您此时将不会进行任何彻底的测试或重构,也不会进行任何类似的事情。您可能只有很短的时间来进行大致估算。我的答案是基于我在相同情况下的经验,因此,如果我的解释不正确,请忽略后面的所有内容。

  • 使用抓取工具可以了解其中有哪些页面以及入站什么。在这方面,即使是基本的linkchecker工具(不是特定的“用于审核的蜘蛛”工具)也将很有用。
  • 制作基本的审核/库存电子表格。这可以像按目录组织文件列表及其最后修改时间一样简单。这将有助于您了解范围,当您进入_OLD和_DELETE之类的目录时,您需要特别注意一下:a)您的评估基于不在这些目录中的内容b)这些目录的存在以及潜在的影响琐事/隐藏的噩梦证明了应以某种方式在客户的出价中说明的更深层次的问题。您不必花费数百万年的时间来枚举_OLD或_DELETE中的可能问题;该信息将纳入最终出价。
  • 鉴于您正在审查听起来完全是基于Web的应用程序,因此即使标准的日志分析器工具也将成为您的朋友。您将能够在电子表格中添加某种“在已访问脚本中排名前10位”的含义。即使脚本嵌入在Flash文件中,因此无法进行抓取,也很有可能通过POST或GET访问这些脚本,并将这些脚本显示在服务器日志中。如果您知道自己有10个高度访问的脚本,而不是100个(反之亦然),那么这将使您很好地了解维护工作的进行方式。

即使在复杂的站点中,我上面概述的内容也可以在一天或一天​​半的时间内完成。既然你打算给答案你的客户是一样的东西“,这是要在对接了巨大的痛苦,这里有一些原因,你会仅仅是涂口红的猪,所以你应该相应出价”或“任何有理智的人都会竞标不要维持而是重新开始,因此您应该相应地竞标”,甚至“这还不错,但是在任何给定的时间范围内这将是一个持续的工作流程,因此相应地竞标” ,重点是他们将要竞标,因此,如果您被直接雇用来进行完整的内容和体系结构审核,则您不需要像以前那样精确。


2
+1这是一个很棒的答案。+5按钮到哪里去了?
工程师

1
TL; DR:除非必须这样做,否则不要让自己陷入困境。:)
jcmeloni 2012年

4

我强烈建议使用“ 有效地使用旧版代码 ” 一书中的模式来重构现有源代码(而不是重写)。

本书详细介绍了在单元测试中有效覆盖遗留代码的几种机制,因此您可以开始安全地重构代码。该书分为几部分,其中一章描述了该方法背后的原理,然后是解决特定问题的几章,例如“需要永远做出改变”,“我没有太多时间并且需要改变它”。 ,以及“我无法将此类纳入测试工具”。这些章节中的每一章都有详细的,经过验证的技术,可以帮助您学习如何在测试中解决现实问题时应用最佳实践。

读这本书给我一种非常真实的感觉,即“我们并不孤单”……我们中的许多人,或者也许我们所有人,都在使用难以管理的复杂代码库。书中列出的技术给了我很多希望,而且我个人几乎可以立即应用它们。

Joel Spolsky的博客文章很好地解释了为什么最好的办法是保留现有的有效代码库,而不是从头开始。我从这篇文章中选择了一个引言进行总结,但它读起来很棒。

“有一个微妙的原因,程序员总是想扔掉代码并重新开始。原因是他们认为旧代码是一团糟。这是一个有趣的观察:他们可能是错误的。他们认为旧代码是有原因的。代码一团糟是因为编程的基本原理:

阅读代码比编写代码更难。” -http://www.joelonsoftware.com/articles/fog0000000069.html


4
+1。在回应乔尔的评论时:“不应该流血。” 因为我认为问题不是内在的。我认为部分原因是许多人编写劣质代码而不在乎,而其他许多人则编写了相当不错的代码,但都遵循“自我记录代码”的概念……这只是简单的BS:有人可能会恭维所有人都希望自己拥有自己的编码风格,但是在涉及公共代码库时,只是产生注释,就像没有明天一样。不伤人。最后,有些人必须在紧凑的时间预算下使事情在旧版代码库中运行。
工程师

2

在典型的Java代码库中,我将考虑使用PMD,FindBugs或Sonar之类的工具,然后尝试了解工具报告(无效代码,未记录代码,重复代码等)。

根据报告,我将尝试查找应用程序/站点的不同层(业务层,数据库,SQL等)。

如果各层是耦合的(servlet中的html,java代码中的sql),那么我首先将这些步骤解耦,然后将其视为隔离,您可以在每个步骤的最后提交(通过开始一个分支然后进行合并) 。


1
谢谢。尽管您的答案在某种程度上是Java特定的,但有趣的是您可以看到分层的方法……可以说剥洋葱了。需要考虑的事情。
工程师

1

从您的描述看来,这段代码已经达到了无法维护的状态,这意味着最好的方法可能是完全重写。如果有能够保持凌乱的代码库可维护的高质量工具,则开发人员的薪水将少很多。可以从文件夹中清理掉旧的不需要的代码,但这是一项手动任务,如果没有不合理的时间,您可能将一无所获。我只是在这里猜测,但是我敢打赌,工作代码本身与文件结构一样混乱,这意味着即使您设法将代码库调整为有效的工作代码,它仍将是一场噩梦更新或修复任何内容。

我要强调的是,使现有代码处于可维护状态所需的工作将等于或大于重新编写代码的工作。维护一切的一部分就是知道什么时候“把它带到棚子里开枪”。


通常,我会百折不挠地与您同在。但是在这种情况下(至少现在是这样),我只需要支付维护站点的工作费,而不是花数周时间进行更广泛的大修。另外,即使我现在想,也无法跟上我的进度,也不想随身携带其他合同,因为我每周的工作量受到明显限制-我的主要合同必须履行每周至少40小时。
工程师

1
不赞成折腾并重写!来自joelonsoftware.com/articles/fog0000000069.html ...“有一个微妙的原因,程序员总是想扔掉代码并重新开始。原因是他们认为旧代码是一团糟。这是一个有趣的发现:他们可能是错的。他们认为旧代码是一团糟的原因是由于编程的基本原理:阅读代码比编写代码难。相反,我强烈建议您重构:amazon.ca/Working-Effectively-Legacy-Michael-Feathers/dp/…–
Kyle Hodgson

1
@KyleHodgson有时代码实际上是一团糟,而当您在阅读代码之前发现代码是一团糟时,就该重新开始了。
Ryathal 2012年

是的,尽管那本书看起来值得一读,但我认为这没有那么明确。它确实在很大程度上取决于代码库的大小/复杂性以及可用于执行该工作的温暖的主体。
工程师

1

网络搜寻器可以帮助您确定可访问的URL。特别是如果它足够聪明,可以从Flash或JavaScript中提取链接。有了网页列表后,请遍历网页并列出它们引用的文件。在此过程之后剩下的所有内容都应视为无效代码。


1
我非常不同意你的最后一句话。搜寻器只能找出哪些页面以一个或多个起点作为有向图链接在一起。但是,当我们谈到一个网站时,也有所谓的“登录页面”,它们链接到其他页面,但是没有指向它们的链接。另外,管理界面的某些旧部分可能也与其他页面断开了连接。我目前有一个此类项目。
scriptin

0

注意:在您询问代码本身的用法时,我强调了数据库的用法。在我提到的每一点上,答案仍然适用于两种情况。

在上一段中,您已经部分回答了自己的问题:查看应用程序运行时访问了什么。

  1. 您可能想对数据库进行概要分析,并要求概要分析器记录一天中的所有查询。它将为您提供最常用的数据库对象的概述,但不会告诉您从未使用过哪些数据库对象。同样,您仍然必须小心结果:例如,表可能仅通过存储过程使用,但是当您查看分析器中的查询时,看起来好像根本就没有使用该表。

  2. 查看源代码,搜索查询会更有用,并且在收集所有查询之后,您可以对数据库的使用情况有一个很好的了解,而不是频率(在探查器中很方便),而在使用/不使用方面使用的表。遗憾的是,对于编写不好/未维护多年的代码库,它可能非常困难且容易出错,尤其是在动态构造查询的情况下(想象一下一种方法,其中select使用参数作为表名;您如何仅查看源代码就可以知道参数的可能值是什么?)。

  3. 静态分析和某些编译器也可能会显示错误的代码,但仍然无法为您提供所需的答案。

  4. 对数据本身或数据库元数据的分析可以揭示一些有趣的信息。例如,很容易断言,如果该表LogonAudit(uniqueidentifier LogonAuditId, datetime LogonEvent, ...)包含2006年至2009年每天的10000条记录,并且从9月18 起没有记录,则该表不再使用。起则不再使用该表。包含缩进为只读的数据的表。

这四个点将为您提供使用表的列表。剩下的要么不使用,要么不使用。您可以声明并测试它们,但是如果没有良好的单元测试覆盖率,这将不容易。任何“简单”的方法也会失败。例如,如果您有一个products_delme_not_used表,则可以断言该表根本没有使用,并在代码中检查“ products_delme_not_used”。这是乐观的:在旧代码库中找到这样的DailyWTF候选对象并不罕见:

// Warning: WTF code below. Read with caution, never reuse it, and don't trust
// the comments.

private IEnumerable<Product> GetProducts()
{
    // Get all the products.
    return this.GetEntities<Product>("PRODUCT");
}

private IEnumerable<T> GetEntities<T>(string tableName)
{
    // Everyone knows that SQL is case sensitive.
    tableName = tableName.ToLower();

    if (tableName == "user" || tableName == "product")
    {
        // Those tables were renamed recently in the database. Don't have time
        // to refactor the code to change the names everywhere.
        // TODO: refactor the code and remove this `if` block.
        tableName += "s";
    }

    if (this.IsDelme(tableName))
    {
        // We have some tables which are marked for deletion but are still
        // used, so we adjust their name.
        tableName = this.Delme(tableName);
    }

    return this.DoSelectQuery<T>("select top 200 * from " + tableName);
}

private bool IsDelme(string name)
{
    // Find if the table is among candidates for removal.
    List<string> names = this.Query<string>("select Names from DelmeTables");
    return names.Contains(name);
}

private string Delme(string name)
{
    // Return the new name for a table renamed for deletion.
    return string.Join("_", new [] { name, "delme", "not", "used" });
}

您能找出这段程式码实际使用了吗 products_delme_not_used表吗?

如果我是你我会:

  1. 保留所有数据库对象,
  2. 重构整个应用程序(如果值得的话),
  3. 记录(重构时)应用程序,尤其是数据库使用情况。

当您完成最后两个步骤时,您可能会更好地了解数据库的用法,这将有助于确定不再使用的表的名称,并可能或多或少地安全地删除它们。


0

在我看来,您需要获取足够的信息来创建报价,因此我将专注于此工作。

我将尝试确定该站点涉及多少个用例。通常,您可以了解该站点的大小和复杂程度,以及重新创建或维护该站点/应用程序将花费多少时间。

是的,的确有时不再使用代码,这会使应用程序看起来比实际的要大一点,但是我认为这最多不会对数字产生超过20%的影响,所以我不用担心那部分。

查看源代码,网页和数据库表应该可以帮助您发现这一点。

您可能还需要考虑限制用于保护自己的预定费用,每月花费在该项目上的小时数。

至于发现正在使用和未使用的内容,实际上没有简单的方法。代码分析工具可能会有所帮助,但是由于您要处理的是这样一个混杂的问题,所以我认为没有任何一个工具可以提供帮助。对于每个特定领域,您可能可以找到可能会有所帮助的代码分析工具。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.