我是一名初级软件工程师,接到的任务是接管旧系统。根据我的初步评估,这个系统有几个问题。
- 意大利面条代码
- 重复代码
- 10k 行及以上的
- 类使用 log4j 误用和过度记录
- 糟糕的数据库表设计
- 缺少源代码控制 ->我已经为这个
- 丢失的文档设置了 Subversion ->我对业务规则一无所知,除了阅读代码之外,
我应该如何提高系统的质量并解决此类问题?我可以考虑使用静态代码分析软件来解决任何不良的编码实践。
但是,它无法检测到任何不良的设计问题或问题。我该如何逐步解决这些问题?
I am a junior software engineer who've been given a task to take over a old system. This system has several problems, based on my preliminary assessment.
- spaghetti code
- repetitive code
- classes with 10k lines and above
- misuse and over-logging using log4j
- bad database table design
- Missing source control -> I have setup Subversion for this
- Missing documents -> I have no idea of the business rule, except to read the codes
How should I go about it to enhance the quality of the system and resolve such issues? I can think of using static code analysis software to resolve any bad coding practice.
However, it can't detect any bad design issues or problems. How should I go about resolving these issues step by step?
发布评论
评论(14)
获取并阅读有效使用旧代码。它正是针对这种情况。
正如其他人也建议的那样,为了重构,您需要一套可靠的单元测试。然而,遗留代码通常很难按原样进行单元测试,因为它没有被编写为可单元测试的。因此,您需要首先进行重构以允许单元测试,这将允许您开始重构……这是一个糟糕的问题。
这就是本书将为您提供帮助的地方。它提供了许多实用建议,说明如何通过尽可能少且最安全的代码更改来使设计糟糕的代码单元可测试。自动重构也可以在这里帮助你,但是书中描述的一些技巧只能手动完成。然后,一旦第一组单元测试就位,您就可以开始逐步重构,以获得更好、更易于维护的代码。
更新:有关如何接管遗留代码的提示,您可以找到此我之前的回答很有用。
正如 @Alex 指出的,单元测试对于理解和记录代码的实际行为也非常有用。当有关系统的文档不存在或已过时时,这尤其有用。
Get and read Working Effectively With Legacy Code. It deals exactly with this situation.
As others have also advised, for refactoring you need a solid set of unit tests. However, legacy code is typically very difficult to unit test as is, since it has not been written to be unit testable. So you need to refactor first to allow unit testing, which would allow you to start refactoring... a bad catch.
This is where the book will help you. It gives lots of practical advice on how to make badly designed code unit testable with the minimal, and safest possible, code changes. Automatic refactorings can also help you here, but there are tricks described in the book which can only be done by hand. Then once the first set of unit tests are in place, you can start gradually refactoring towards better, more maintainable code.
Update: For hints on how to take over legacy code, you may find this earlier answer of mine useful.
As @Alex noted, unit tests are also very useful to understand and document the actual behaviour of the code. This is especially useful when documentation about the system is nonexistent or outdated.
首先注重稳定。在应用程序周围拥有某种稳定的环境之前,您无法增强或重构。
一些想法:
不惜一切代价,谨防重写整个事情的诱惑。在这种情况下,这几乎从来都不是正确的做法。如果它有效,就集中精力让它发挥作用。
作为初级开发人员,不要害怕寻求帮助。正如其他人所说,有效处理遗留代码是一本值得一读的好书,Martin Fowler 的重构也是如此。
祝你好运!
Focus on stability first. You can't enhance or refactor until you have some kind of stable environment in-place around the application.
Some thoughts:
At all costs, beware the temptation to just rewrite the whole thing. Its almost never the right thing to do in this situation. If it works, concentrate on keeping it working.
As a junior developer, don't be afraid to ask for help. As others have said, Working Effectively With Legacy Code is a good book to read, as is Martin Fowler's Refactoring.
Good luck!
首先,不要修复没有损坏的东西。只要您要接管的系统能够正常工作,就不要管功能。
然而,当涉及到可维护性时,该系统显然已被破坏,因此这就是您要解决的问题。如上所述,首先编写一些测试,将源代码备份到 cvs 中,然后首先清理小部分,然后是较大的部分,依此类推。在充分了解系统的工作原理之前,不要解决更大的架构问题。只要您不亲自深入研究代码,工具就不会为您提供帮助,但是当您深入研究代码时,它们确实会提供很大帮助。
请记住,没有什么是“完美的”。不要过度设计。遵守KISS 和YAGNI 原则。
编辑:
添加了 YAGNI 文章的直接链接First, don't fix what isn't broken. As long as the system you are to take over works, leave functionality alone.
The system is obviuosly broken when it comes to maintainability, however, so that is what you tackle. As mentioned above, write some tests first, get the source backed up in a cvs, and THEN start by cleaning up small pieces first, then the larger ones and so on. Do NOT attack the bigger architectural issues until you have gained a good understanding of how the system works. Tools won't help you as long as you don't dive into the code yourself, but when you do, they do help a lot.
Remember, nothing is "perfect". Don't over-engineer. Obey the KISS and YAGNI principles.
EDIT:
Added direct link to YAGNI article您的问题 #7 是迄今为止最重要的。只要您不知道系统应该如何运行,所有技术考虑因素都是次要的。每个人都在建议单元测试 - 但是如果您无法区分想要的行为和不需要的行为,如何编写有用的测试呢?
因此,在开始接触代码之前,您必须从用户的角度了解系统:与用户交谈,观察他们使用系统,编写用例级别的文档。
是的,我认真地建议您花几天甚至几周的时间而不更改任何代码。因为现在,你所做的任何改变都可能在你没有意识到的情况下破坏一切。
一旦您了解了该应用程序,您至少会知道哪些功能对于测试很重要(手动或自动)。
Your issue #7 is by far the most important. As long as you have no idea how the system is supposed to behave, all technical considerations are secondary. Everyone is suggesting unit tests - but how can you write a useful test if you can't distinguish between wanted and unwanted behaviour?
So before you start touching the code, you have to understand the system from the user's point of view: talk to users, observe them using the system, write documentation on the use case level.
Yes, I am seriously suggesting that you spend days, more likely weeks, without changing a single line of code. Because right now, any change you make is likely to break things without you realizing it.
Once you understand the app, you'll at least know which functionality is important to test (manually or automated).
首先编写一些单元测试,并确保它们通过。然后,随着您所做的每次重构更改,请继续确保测试不断通过。然后您就可以确信您的应用程序对外界的行为没有改变。
这还有一个额外的好处,即测试将始终存在,因此对于任何未来的更改,测试仍然应该通过,以防止新更改中的任何回归。
Write some unit tests first, and make sure they pass. Then with each refactoring change you make, just keep making sure the tests keep passing. Then you can be confident that your application behaviour to the outside world hasn't changed.
This also has the added benefit that the tests will always be there, so for any future changes the tests should still pass, guarding against any regressions in the new changes.
首先也是最重要的,确保您安装了源代码控制系统,并且所有源代码都已版本化并且可以构建。
接下来,您可以尝试为系统的核心部分编写单元测试。从那里开始,当您有了或多或少可靠的回归测试时,您实际上可以继续进行重构。
当我遇到混乱的代码库时,我通常会从重命名命名不当的类型和方法开始,以更好地反映其最初的意图。接下来,您可以尝试将大型方法拆分为较小的方法。
First and foremost, make sure you have source control system installed and all source code is versioned and can be built.
Next, you can try writing unit test for core parts of your system. From there, when you have a more or less solid body of regression tests, you can actually proceed with refactoring.
When I encounter messy codebase, I usually start with renaming poorly-named types and methods to better reflect their initial intent. Next you can try splitting huge methods into smaller ones.
请记住,这个遗留系统及其所有意大利面条代码目前都可以运行。不要仅仅因为事情看起来不够漂亮就去改变它们。专注于稳定性、新功能和功能在将旧代码从左右和中间撕掉之前先熟悉一下。
Keep in mind that this legacy system, with all it's spaghetti code, currently works. Don't go changing things just because they don't look as pretty as they should. Focus on stability, new features & familiarity before ripping old code out left right and centre.
首先,我要说的是有效地使用遗留代码可能是一个真正的一本值得一读的好书,通过一分钟内三个答案来判断。
您可能会被这一问题困扰。如果您尝试更改现有的数据库设计,您可能会致力于重新设计整个系统并为现有数据编写迁移工具。好好地待着吧。
Firstly, let me say that Working Effectively with Legacy Code is probably a really good book to read, judging by three answers within a minute of each other.
This one, you are probably stuck with. If you try to change an existing database design you are probably committing yourself to redesigning the whole system and writing migration tools for the existing data. Leave well alone.
我对这个问题的标准答案是:重构唾手可得的成果。在这种情况下,我倾向于参加 10K 线课程之一,并寻找Sprout 的机会类,但这只是我自己的倾向;您可能会更愿意先更改其他内容(设置源代码控制是优秀的第一步!)测试您可以做的事情;重构无法测试的内容,一步一步地改进。
请记住,随着您的进步,您正在使事情变得更好;如果你只关注事情仍然有多糟糕,你可能会变得灰心丧气。
My standard answer to this question is: Refactor the Low-hanging Fruit. In this case, I'd be inclined to take one of the 10K-line classes and seek out opportunities to Sprout Class, but that's just my own proclivity; you might be more comfortable changing other things first (setting up source control was an excellent first step!) Test what you can; refactor what can't be tested, take a step at a time, and make it better.
Keep in mind as you progress how much better you are making things; if you concentrate only on how bad things still are, you're likely to become discouraged.
正如其他人所指出的,不要仅仅为了让它变得更漂亮而改变一些有用的东西。引入错误的风险很大。
我的理念是:由于我必须进行更改以满足新的需求或修复报告的错误,因此我尝试使我必须更改的代码片段更干净一些。无论如何,我都必须测试更改后的代码,所以现在是以少量额外成本进行一些清理的好时机。
基本的设计更改是最困难的,必须在必须进行足够大的更改以测试所有更改的代码的情况下进行。
改变糟糕的数据库设计是最困难的,因为设计糟糕的表可能会被许多程序使用。对数据库的任何更改都需要更改读取或写入数据库的每个程序。实现此目的的最佳方法通常是尝试减少访问数据库任何给定部分的位置数量。举个简单的例子:假设有20个地方读取客户记录并计算客户账户余额。将其替换为一个读取数据库并返回总数的函数,以及对该函数的二十次调用。现在,您可以更改客户记录的架构,并且只需更改一段代码,而不是 20 段。原理很简单,但实际上,访问给定记录的每个函数不太可能都做同样的事情。即使最初的程序员笨拙到将相同的代码编写了 20 次(并非不可能——我见过很多这样的情况),真实的情况可能不是他写了 1 个函数 20 次,句号,而是他写了函数A 20 次,功能 B 12 次,功能 C 4 次,以此类推。
As others have noted, don't change something that works just to make it prettier. The risk that you will introduce errors is great.
My philosophy is: As I have to make changes to satisfy new requirements or to fix reported bugs, I try to make the piece of code that I have to change a little cleaner. I'm going to have to test the changed code anyway, so now is a good time to do a little clean-up at small additional cost.
Fundamental design changes are the toughest and must be saved for occasions where you have to make a big enough change that you would be testing all the changed code anyway.
Changing bad database design is hardest of all because the poorly designed tables are likely used by many programs. Any change to the database requires changing every program that reads or writes it. The best way to accomplish this is usually to try to reduce the number of places that access any given part of the database. To take a simple example: Suppose there are 20 places that read through customer records and calculate the customer account balance. Replace this with one function that reads the database and returns the total, and twenty calls to that function. Now you can change the schema for the customer records and there is only one piece of code to change instead of 20. The principle is simple enough, but in practice it is unlikely that every function that accesses a given record is doing the same thing. Even if the original programmer was clumsy enough to write the same code 20 times (not unlikely -- I've seen plenty of that), the real situation is probably not that he wrote 1 function 20 times, period, but that he wrote function A 20 times, function B 12 times, function C 4 times, etc.
有效使用旧代码可能会有所帮助。
Working Effectively With Legacy Code might be helpful.
设计问题很难发现。首先要开始的是了解应用程序的设计。我发现使用 UML 或流程图来绘制图表很有用,任何能够传达设计和应用程序工作的东西都可以。
从那里我会进入更多细节,并问自己这样的问题“我会这样做吗”,还有哪些其他选择。很容易看到代码债务,即我们因做出错误选择而获得的债务,一如既往的糟糕,但有时还涉及其他因素,如预算、时间、资源的可用性等。您必须问这样的问题:值得重构一个可以工作但设计糟糕的应用程序。
如果有许多即将推出的新功能、更改、错误修复等,我会说重构是件好事,但如果应用程序很少更改并且稳定,那么也许保持原样是更好的方法。
另一个需要注意的方面是,如果代码被另一个应用程序用作服务或模块,那么重构可能首先意味着在作为接口的代码周围创建一个存根,一旦明确定义并有单元测试来证明这一点工作。您可以选择任何技术来填写详细信息。
Design issues are very difficult to catch. The first place to start is understanding the design of the application. I find it useful to diagram using either UML or a process flow diagram, anything works that communicates the design and working for the application.
From there I go into more detail, and ask myself the questions "Would I have done it this way", what other options are there. It is easy to see code-debt, i.e. the debt that we get from making bad choices, as always bad, but sometimes there are other factors involved like budget, time, availability of resources etc. Their you have to ask the question if it is worth refactoring a working but bad designed application.
If there are many upcoming new features, changes, bug fixes, etc I would say it is good to refactor, but if the application rarely changes and is stable, then maybe leaving it as is is a better approach.
Another sidepoint to note, is that if the code is used by another application as a service or module, then refactoring might first mean create a stub around the code that servers as the interfaces, once that is defined clearly and has unit test to prove it work. You can choose any technology to fill in the details.
关于这个主题的一本好书是 Michael Feathers 的《Working effective with Legacy Code》(2004 年)。它经历了进行小的改变,同时努力进行更大的清理的过程。
A good book on this subject is Working Effectively with Legacy Code By Michael Feathers (2004). It goes through the process of making small changes, while working towards a bigger clean up.
首先尝试创建一些可以触发代码中某些操作的单元测试。
在 SVN 中提交所有内容并标记它(如果出现问题,您将有一个逃生舱)。
使用 inCode Eclipse 插件 http://www.intooitus.com/inCode.html 并查找它建议进行哪些重构。检查建议的重构是否适合您的问题。尝试去理解他们。
使用之前创建的单元重新测试。
现在您可以使用 FindBugs 和/或 PMD 来检查其他细微问题。
如果一切正常,您可能想再次入住。
我还会尝试阅读源代码,以检测某些可以应用模式的情况。
Try to create some unit tests first that can trigger some actions in your code.
Commit everyting in SVN and TAG it (in case that something goes bad you'll have an escape pod).
Use inCode Eclipse plugin http://www.intooitus.com/inCode.html and look for what refactorings it proposes. Check if the refactorings proposed seem ok for your proble. Try to understand them.
Retest with the units created before.
Now you can use FindBugs and/or PMD to check for other subtle issues.
If everything is oka you might want to check-in again.
I'd also try reading the source in order to detect some cases where patterns can be applied.