百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 技术资源 > 正文

我见过的最糟糕代码

lipiwang 2025-05-23 18:24 4 浏览 0 评论

本文最初发布于 jesuisundev.com 网站,经原作者授权由 InfoQ 中文站翻译并分享。


在本文中,我将向你展示我见过的一些最糟糕的代码,它们被称为“魔鬼代码”,会带来很严重的后果。然而,我们发现通过一些好的实践,你可以很容易规避它们。

“魔鬼代码”

需要改进的代码与所谓的“魔鬼代码”是不一样的


不管使用的是哪种语言,“魔鬼代码”都很糟糕,因为它会危及项目的稳定性和可维护性。在职业生涯中,我见过很多“魔鬼代码”。


当它堆积如山时,你的项目很快就会变成“十八层地狱”的样貌。如果你喜欢到处捅娄子,那么领导看你的眼光也会越来越不一样。

模棱两可和前后矛盾

很久以前,我在一个清晨醒来,被世界末日般的景象吓了一跳。生产环境出现一个很大的错误,所有系统票证莫名其妙地返回“null”。到处都乱成一团。所有人都像无头苍蝇一样到处乱跑。


我跑到公司,冲向工作站,第一步是看 Kibana。但是,没有日志,什么都没有。为了查明原因,我决定追溯票证的创建路径。因此,我必须深入研究系统从诞生到现在创建的那些内部库。经过一番调查,我找到了问题来源之一的一堆文件。


然后,我看到这样的景象:


// use id and expire to get ticket
async function get_ticket(i, expire) {
  return CheckisNotExp(expire).then(async function() {
    var t = await GetTicketModel(i)
    if (t) {
      return t
    } else {
      logger.error(JSON.stringify(t))
      return null
    }
  }).catch(async function(e)  {
      logger.error(JSON.stringify(e))
      return null
})
}

复制代码


真有一个“i”变量吗?这是一个 id,对不对?它是整数还是 UUID?到底是什么到期了?是日期还是时间戳?为什么会有camelCasePascalCasesnake_case?带有 promise 的异步注解和又一个异步注解?如果失败,我们会返回 null?简直是魔鬼啊!


那时,每隔 5 分钟就有一半的公司同事向我发 Skype 消息,索取 ETA 修复。


不过,首先,有人需要知道这里究竟发生了什么。而我意识到,该为这个问题负责的不是一个人,而是三个人。很久以前,其中两个人离开了公司,而第三个人今天早上还没来公司。


根据 Git 的记录,这三个人碰这个文件的时间各自差了很久。因此,他们留下了不一致的代码、不同的样式、不一样的 ECMAScript 版本和不同的 promise 处理方式。


不管怎样,在这段代码中,一切都是模棱两可的,一切都是不一致的。这是一个绝佳的反面案例,你应该尽一切可能避免这种情况。不用说,代码审查并没有覆盖到这里。


为了解决问题,我们必须快速重写它,更改那些变量和函数的名称,不能再出现歧异。而且,各处的 Async/Await 都要做成相同的方式。


我还要确保自己不会漏掉任何错误,结果再返回一个 null。如果出现什么问题,这些错误肯定要破坏函数。异常应由上面的层来处理。


// hotfix
// @todo rewrite the ticket module entirely
async function getTicket(ticketUuid, ticketExpirationTimestamp) {
  await validTicketExpiration(ticketUuid, ticketExpirationTimestamp)
  const ticket = await getTicketByUuid(ticketUuid)


  return ticket
}

复制代码


最好的解决方案是重写这个模块的一部分。这里的票证验证逻辑很糟糕。但这并不是最要紧的事情。当务之急是找出并修复错误。


在更新代码后,真正的错误开始浮出水面。前一天所做的一个配置更改改变了票证创建行为。返回到先前的配置则可以立即解决这个问题。接下来的一周时间里,有问题的模块被完全重写。

肉酱意面

很久以前,我正在做一个代码干净整齐的产品。作为优质产品,一切都在内部做好了优化。功能是用尽可能少的代码开发的。代码高度重视可读性。由注重整洁代码的工程师管理的代码审查流程确保产品严格遵循所有最佳实践。SOLID、DRY、KISS、YAGNI 和你可以想到的其他首字母缩写词,这里都能见得到。


即使做到这个地步,这个产品的某个特殊部分也会间歇性地崩溃。在一个冲刺期间,我终于设法安排出时间来调查这件事。


很快,我意识到问题不在于产品。那些错误只有一个共同点:一个依赖项。那是一个通过内部工件处理的内部依赖项。


它由另一个团队管理,而且——令人惊讶的是——这段代码不是免费提供的。你必须先获得许可才能看到它。因此,我请求了访问权限来了解到底发生了什么事情。然后,我收到了一条 Slack 消息,问我为什么要访问源码。


“你好!为什么你需要访问这个存储库?”

“这是什么意思?你知道我在这里工作吗?等等,我在路上。”


我突然出现在他面前后,终于拿到访问该项目的权限。


我在其中看到一个文件,大小为 300KB。300KB 的文本,竟然有那么大。它已经有好几年没人碰过了。上次碰过它的那个人,我完全不认识。简直是最可怕的魔鬼。


那是我一生中见过的规模最大的意大利面条代码。篇幅所限,我并没有把所有代码都放在这里。下面的代码只是一部分:


// Thousands of lines of spaghetti codes


if (global.Builder)
{
    module.exports.buildPgs = function(pgs, options, limitNodes = 0)
    {
        var config = options.config || {};
        var builded = [];
        pgs.each(function(pg)
        {
            var supported = pg.prop('tagName') == "INPUT"
                            && pr.attr['name'] == "file"
                            && options.pgs.rel.active == ""
                            && global.FileReader;
            if (!supported || !pg.f || pg.f.length == 0)
                return;
            
            for (var i = 0; i < pg.f.length; i++)
            {
                builded.push({
                    file: pg.f[i],
                    instanceConfig: _.extend({}, config)
                });
                
                if (isFunction(options.before))
                {
                    var returned = options.before(pg.f.path);
                    if (typeof returned === 'object' && global.status.in_progress)
                    {
                        if (returned.action == "skip")
                        {
                            var needsSkip = (typeof global.status.in_progress === 'boolean' && global.status.in_progress)
                                            || _.hasAny("cancel", Global._quotes.BAD_DELIMITERS)
                                            || str.indexOf(Global._delimiter) > -1;
                            
                            if(needsSkip) return;
                        }
                        else if (typeof returned.config === 'object')
                        {
                            var LOCAL_BUILDER = new global.Builder("/builder/" + options.module + "/" + options.module + );
                            
                            for(var s=p,a=p.matchIndex(o),shift=0,i=0;i<a.length;i++){
                                var deepcopyfile = JSON.parse(JSON.stringify(pg.f[i].getRawValue()));
                                LOCAL_BUILDER.build(deepcopyfile)
                                LOCAL_BUILDER.onmessage = global.Notification("buildPg", deepcopyfile);
                            }
                        }
                    }
                }
            }
        });
    }
}


// Thousands of lines of spaghetti codes

复制代码


我甚至都没有敢去碰它。



在这类情况下,解决方案不是从代码中找出来的。我召开了一次小组会议,向他们介绍具体情况。我的计划也很简单——我们不碰它


我们用一个已经可用的开源模块替换了这个撒旦般的依赖项。与往常一样,这是一个大问题,必须做一些准备工作才能正确插入新的依赖项。


一开始的快速调查已经演变成持续几天的一项艰巨任务。


在会议桌那头,Scrum 主管很生气。讨论得越多,我越觉得想要不碰到该死的东西会非常困难。当我展示我们的处境后,讨论结束了,答案是不行。


“你只需要稍微动一动这个模块,把它修好就行了,然后我们会继续原本的工作。”


因此,我在代码质量和项目可持续性方面做了额外的工作。我说不行,甚至已经做好辞职的准备。他们显然问了其他开发人员。大家都拒绝了。


由于这个问题的严重性,我争取到替换这个模块所需的时间。我为开源依赖项开发了一个小型适配器。然后,我摆脱了那个被诅咒的依赖项。


此后,那个产品一切顺利,运行正常。


开发人员经常会抱怨意大利面条代码,这是有充分理由的。这是你能见过的最糟糕的代码。但是,无需大量投资即可确保你能避免这种情况。

驱魔

一开始,本文想写的是一个最佳实践的列表。


“作为开发人员,为什么以及如何应用最佳实践。”


不过上面这个标题很容易像大剂量安眠药一般令人昏昏欲睡,此外我出于两个原因改变了计划。


首先,对于我,特别是对你来说,先谈论后果会有趣很多。对开发人员来说,这很重要,因为这就是魔鬼代码的起源。此外,如果你可以为我的遭遇会心一笑,那也很好。


其次,互联网上已经有很多关于这个主题的文章。它们都有一个共同点,就是它们的内容都是从两本书中摘出来的。这两本书培养了几代开发人员。——罗伯特·马丁的《代码整洁之道》、史蒂夫·麦康奈尔的《代码大全》


你是否真的要缩短代码审查时间,并且再也不想搞出什么魔鬼代码?直接看原始资料就行,花点时间好好看完这两本书。


我发现《代码大全》的方法更易读、更实用。但是,尽管《代码整洁之道》非常复杂,但它教给我的知识不亚于甚至超过了《代码大全》。前者里面使用的代码是 Java 和 C++,但是谁在乎具体的语言呢?你在这本书里学到的是规则和编程理念。


用代码审查来验证代码是好事情。但是,如果你不确定为什么它是好的代码,那么到头来还是会出现你经历过的魔鬼代码。


原文链接:


https://www.jesuisundev.com/en/the-worst-pieces-of-code?fileGuid=vwdYpVcQyqDCYhCr

相关推荐

WIN10系统如何启用net Framework功能

当我们安装一些办公软件或是设计软件时,需要.netFramework运行环境,如果没有安装这个运行环境,软件就会无法运行了。那在哪启用呢?下面,我来和大家分享一下第一步,点击开始--设置--在查找设...

玩游戏怎能没有.NET Framework 3.5 但你会安装它吗?

前段时间NVIDIA发布的最强“煤气灶”引发的不仅仅是显卡价格的剧烈变动(其实也没啥,老的不降价,新的还死贵),还有就是随之而来的一大波游戏,一部分在STEAM平台上搞促销,另外一部分则是不停的放烟雾...

.Net Framework还是.Net Core?

大家都知道,微软在2016年推出了.NetCore框架。后来为了统一,在.NetFramework到了V4.8后便不再更新,同时.NetCore更名为.Net,版本从5.0开始,目前最新版本是....

.Net Framework详解

相信有不少小伙伴遇到过这种情况:安装软件时提示.NetFramework未安装导致软件无法打开,或者需要安装.NetFramework4.0以上的组件。那.NetFramework是什么呢?....

远离报错烦恼!深入全面掌握.NET Framework

由于Windows系统对.NETFramework这一系统组件有着极为特殊的要求,而部分应用软件及游戏对其的依赖性也近乎达到了驱动级的水准,使用或安装不当会遭遇许多“未知”的问题,因此如何掌握.NE...

系统小技巧:深入全面掌握.NET Framework

由于Windows系统对.NETFramework这一系统组件有着极为特殊的要求,而部分应用软件及游戏对其的依赖性也近乎达到了驱动级的水准,使用或安装不当会遭遇许多“未知”的问题,因此如何掌握.NE...

小菜鸟学Java--如何拿下数组

前面谈及到了引用数据类型,数组就是一种,它还是比较重要的一种,本篇就来详细说说它和向量。数组先来说说数组。那么什么是数组呢?它是在数组的基础上的。数组是一种常用的引用数据类型,相同数据类型的元素按照一...

Java Class 类文件格式看这一篇就够了

本文将揭开JavaClass文件的神秘面纱,带你了解Class文件的内部结构,并从Class文件结构的视角告诉你:为什么JavaClass字节码文件可以“写一次,遍地跑”?为什么常量池的计数从1开...

八种经典排序算法总结,妈妈再也不用担心我不会了

前言算法和数据结构是一个程序员的内功,所以经常在一些笔试中都会要求手写一些简单的排序算法,以此考验面试者的编程水平。下面我就简单介绍八种常见的排序算法,一起学习一下。一、冒泡排序思路:比较相邻的元素。...

Java 面试题问与答:编译时与运行时

在开发和设计的时候,我们需要考虑编译时,运行时以及构建时这三个概念。理解这几个概念可以更好地帮助你去了解一些基本的原理。下面是初学者晋级中级水平需要知道的一些问题。Q.下面的代码片段中,行A和行B所标...

Java、Set、Map集合框架知识大全,收藏备用

前言Java集合框架的知识在Java基础阶段是极其重要的,我平时使用List、Set和Map集合时经常出错,常用方法还记不牢,于是就编写这篇博客来完整的学习一下Java集合框架的知识,如有遗漏和错误,...

Java反射+注解实现Entity类与Dto类相互转换

作者:Mr_Precious链接:https://www.cnblogs.com/scale-lai/p/10164634.html序言近期在工作中管理代码时发现,在项目中从Dao层到Service层...

Java 最细的集合类总结

数据结构作为每一个开发者不可回避的问题,而Java对于不同的数据结构提供了非常成熟的实现,这一个又一个实现既是面试中的难点,也是工作中必不可少的工具,在此,笔者经历漫长的剖析,将其抽丝剥茧的呈现出...

我见过的最糟糕代码

本文最初发布于jesuisundev.com网站,经原作者授权由InfoQ中文站翻译并分享。在本文中,我将向你展示我见过的一些最糟糕的代码,它们被称为“魔鬼代码”,会带来很严重的后果。然而,我...

Java面试中最容易忽略的细节,你中了几个?避坑指南送你

今日分享开始啦,请大家多多指教~学习Java要明确自己的需求,知道自己要做什么;分析思路,知道自己要怎么做。确定步骤,每一个思路部分用到哪些方法和对象。最后用代码实现,用具体的java语言把思路体现出...

取消回复欢迎 发表评论: