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

编码 10000 个小时后,开发者悟了:“不要急于发布!”

yuyutoo 2024-10-12 00:32 1 浏览 0 评论

【CSDN 编者按】在软件开发的道路上,时间是最好的老师。根据“一万小时定律”,要成为某个领域的专家,通常需要大约一万小时的刻意练习。本文作者身为一名程序员,也经历了一万小时的编程,最终悟出了一个道理:慢即是快,重视架构设计和代码质量,确保每一行代码都经得起时间的考验。

作者 | Sotiris Kourouklis 翻译 | 郑丽媛
出品 | CSDN(ID:CSDNnews)

我已经做了 7 年多的程序员了,在这期间,我在后端、前端和 DevOps 方面参与了无数个项目。尽管如此,我并不认为自己是一名伟大的程序员;有很多人不仅比我聪明,经验也更丰富。不过这些年来我也总结出了一些经验,让我在编程道路上不断进步,并构建出更可靠且易于维护的软件。

“学会放慢速度,反而能让我编码更快、发布更多,并在整体上更加高效。”

这不仅仅是多年的编程经验,也是我从生活中学到的教训:你必须时刻保持缓慢,不要急躁。


软件开发中的头号问题

很多人在开始编程时,总认为伟大的程序员就像会魔法,他们能以一种无人能懂的独特方式去构建应用程序——但这与事实完全不符。如果仔细查看那些伟大程序员的代码,你会发现它们其实非常简单、很好理解。

不是说在开发应用程序时使用快速、炫酷或使用最尖端的技术,你才能被认为是一名优秀的程序员。管理者也常常犯这个错误,他们老是根据一些不切实际的标准来进行招聘。在我看来,要求应聘者从零开始构建一个应用程序的编程测试是一种相当糟糕的评估方法,它无法全面反映一个人的能力。相比之下,白板面试其实更好,因为至少它可以考察你的智商和思维方式。

那么接下来,让我们谈谈每个人都应该关注但常常忽视的头号问题:

开发者体验

在绝大多数项目中,开发者体验是最重要的事情。每个人都希望快速发布以实现盈利,但最终他们往往在一个月内完成了 90% 的应用程序开发,而剩下的 10% 却需要三个月才能完成。

我理解,开发者总是对新项目充满热情,想要尽快展示成果以获得满足感,并让经理满意。虽然短期内这确实能让经理高兴,但从长远来看,每个人都会陷入恐慌,并在 4-5 年后考虑重构甚至从头开始重建那款应用程序——这就是为什么实际创建功能变得非常困难,而将其推向生产更是难上加难,于是便产生了滚雪球效应。

下面让我们来看两个代码示例:两个控制器,用于从数据库中获取热门用户并为其附加表情符号(来自我的一个开源项目 reporanger.xyz 的代码)。

这是从路由调用的控制器,其中包含所有功能以及一个 try-catch 块,用于检查是否存在错误:

// users.controller.tsconst getTrendingUsers = async (_req: Request, res: Response, _next: NextFunction) => { try { const events = await GithubEvent.find({ where: { event_date: MoreThan(new Date(Date.now() - 24 * 60 * 60 * 1000)) }, order: { event_size: 'DESC' }, take: 3, }); const users = await Username.find({ where: { id: In(events.map((event) => event.username_id)) } }); const topUsers = await getTopUsers(3); const trendingUsers = await Promise.all( users.map(async (user) => ({ ...user, emoji: await emojiService.getEmoji(user.score, topUsers), })), ); res.status(200).json({ status: 200, message: 'Trending users fetched successfully', data: trendingUsers, error: '', success: true, }); } catch (error) { res.status(500).json({ status: 500, message: 'Error fetching trending users', data: , error: error.message, success: false, }); }};

下面是同样的控制器,但我们应用了一个简单原则:单一职责原则。

我们将代码拆分成了 4 个更小且可复用的文件:

● user.controller.ts

● user.service.ts

● async.util.ts

● response.util.ts

经过这样的优化后,带来的好处远超我们的想象!

我们可以在应用程序的任何地方重用这些代码。比如,在定时任务中我们可以调用 userService.getTrendingUsers() 来获取热门用户信息。

我们移除了所有 try-catch 语句块,让代码更加简洁。同时,对于每个错误我们都进行了日志记录(例如使用 logger('error', error))。这样一来就可以很容易地创建一个错误服务,将所有错误信息存储到数据库中,为未来的应用场景做好准备。

另外,通过统一所有控制器的响应方式(如使用 resFn)也非常重要,它可以确保所有请求都能以相同的格式返回响应,正如下面示例中的简单泛型所示。

如此一来,开发一个新的控制器就变得轻松十倍,因为我们整个应用程序的编码架构非常一致。即使后来由其他开发者编写代码,也不会影响整体的编码风格。

// user.controller.tsconst getTrendingUsers = asyncFn(async (_req: Request, res: Response, _next: NextFunction) => { const trendingUsers = await usernameService.getTrendingUsers(); resFn(res, { status: 200, message: 'Trending users fetched successfully', data: trendingUsers, error: '', success: true, });});
// user.service.tsconst getTrendingUsers = async () => { const events = await GithubEvent.find({ where: { event_date: MoreThan(new Date(Date.now() - 24 * 60 * 60 * 1000)) }, order: { event_size: 'DESC' }, take: 3, }); const users = await Username.find({ where: { id: In(events.map((event) => event.username_id)) } }); const topUsers = await getTopUsers(3); const usersWithEmoji = await Promise.all( users.map(async (user) => ({ ...user, emoji: await emojiService.getEmoji(user.score, topUsers), })), ); return usersWithEmoji;};
// async.util.tsexport const asyncFn = (fn: asyncPropsFunction) => async (req: Request, res: Response, next: NextFunction) => { try { await fn(req, res, next); } catch (error) { logger('error', error); next(error); }};
// response.util.tsexport const resFn = (res: Response, { status, error, data, message, success }: IResponse<any>) => { const suc = success !== undefined ? success : true;
res.status(status).json({ error, data, message, success: suc, status, });};
// response.interface.tsexport interface IResponse<T> { status: number; message: string; data: T | any; error: string; success: boolean;}

设想一下:假如我们从一开始就没有实现良好的架构,那么在未来想要对应用程序做一些小的改动时会怎样呢?哪怕只是想记录错误,我们也需要去每一个控制器中添加 logger('error', error);或者我们还想在响应中增加一个额外字段,比如 metadata?那必将会是一场噩梦。


首先进行重构

我觉得,重构应当在编写代码之前就做好:因为每个应用程序最终都需要重构。例如,对于一个拥有超过 70,000 行代码的大型软件项目来说,重构可能需要 30-40 个小时,在此过程中还会引入大量错误和 bug。

最终,你可能会不小心破坏应用程序,或者再花费 20 个小时进行测试。而且,当你的项目达到如此大的规模时,重构效果也不会像一开始就做重构那么理想。

我的建议是,在最初投入 40-50 个小时来规划和重构。只需创建几个控制器,集思广益地考虑如何在未来扩展这些控制器,然后进行重构,之后再继续开发。我知道,一开始你的经理可能会抱怨,因为你花了 50 个小时编码,但几乎没有什么实质性成果可以展示,只有一个能够良好扩展但没人能立即理解的架构。但如果条件允许的话,我觉得还是应该这样做。这不仅会为你自己,也会为未来的开发者省去很多麻烦。

单元测试同样非常重要。不要过分追求覆盖率,保持 60% 以上的覆盖率就足够了,这将在未来帮你避免大量潜在的 bug。


提交前检查

这一点非常重要。对于 JavaScript/TypeScript 项目,我们可以用 Husky。当然,针对不同的语言或框架还有很多其他选择。简单来说,Husky 是一个工具,它会在你提交代码之前运行一些命令。如果出现错误,那提交就不会通过。下面是一个来自 reporanger.xyz 的配置示例:

1、Lint Check(代码风格检查)

2、Run Tests(运行测试)

3、Prettify(代码格式化)

这三个简单的步骤,就能让你的代码库质量提升 10 倍。

#!/bin/shnpx eslint --max-warnings=0 src api/src || { echo "ESLint check failed. Commit aborted." exit 1}
cd ./api && npx jest || { echo "Tests failed. Commit aborted." exit 1}
cd .. && npx prettier --write .git update-index --again


简而言之

有很多方法可以用来改进你的代码,而我上面提到的这些做法其实并不难实现。尤其现在有了大模型(LLM)的帮助,很多问题往往不是你能不能做,而是你是否愿意去做。

抛开无聊的情绪,你会发现这样做会带来很多好处。开始带着热爱去编码,而不仅仅是为了钱。如果你能做到这一点,你会赚到更多的钱,让你的同事开心,你的经理也会感谢你,因为——编程不仅仅是写代码,更是一种架构设计。

相关推荐

MySQL5.5+配置主从同步并结合ThinkPHP5设置分布式数据库

前言:本文章是在同处局域网内的两台windows电脑,且MySQL是5.5以上版本下进行的一主多从同步配置,并且使用的是集成环境工具PHPStudy为例。最后就是ThinkPHP5的分布式的连接,读写...

thinkphp5多语言怎么切换(thinkphp5.1视频教程)

thinkphp5多语言进行切换的步骤:第一步,在配置文件中开启多语言配置。第二步,创建多语言目录。相关推荐:《ThinkPHP教程》第三步,编写语言包。视图代码:控制器代码:效果如下:以上就是thi...

基于 ThinkPHP5 + Bootstrap 的后台开发框架 FastAdmin

FastAdmin是一款基于ThinkPHP5+Bootstrap的极速后台开发框架。主要特性基于Auth验证的权限管理系统支持无限级父子级权限继承,父级的管理员可任意增删改子级管理员及权限设置支持单...

Thinkphp5.0 框架实现控制器向视图view赋值及视图view取值操作示

本文实例讲述了Thinkphp5.0框架实现控制器向视图view赋值及视图view取值操作。分享给大家供大家参考,具体如下:Thinkphp5.0控制器向视图view的赋值方式一(使用fetch()方...

thinkphp5实现简单评论回复功能(php评论回复功能源码下载)

由于之前写评论回复都是使用第三方插件:畅言所以也就没什么动手,现在证号在开发一个小的项目,所以就自己动手写评论回复,没写过还真不知道评论回复功能听着简单,但仔细研究起来却无法自拔,由于用户量少,所以...

ThinkPHP框架——实现定时任务,定时更新、清理数据

大家好,我是小蜗牛,今天给大家分享一下,如何用ThinkPHP5.1.*版本实现定时任务,例如凌晨12点更新数据、每隔10秒检测过期会员、每隔几分钟发送请求保证ip的活性等本次分享,主要用到一个名为E...

BeyongCms系统基于ThinkPHP5.1框架的轻量级内容管理系统

BeyongCms内容管理系统(简称BeyongCms)BeyongCms系统基于ThinkPHP5.1框架的轻量级内容管理系统,适用于企业Cms,个人站长等,针对移动App、小程序优化;提供完善简...

YimaoAdminv3企业建站系统,使用 thinkphp5.1.27 + mysql 开发

介绍YimaoAdminv3.0.0企业建站系统,使用thinkphp5.1.27+mysql开发。php要求5.6以上版本,推荐使用5.6,7.0,7.1,扩展(curl,...

ThinkAdmin-V5开发笔记(thinkpad做开发)

前言为了快速开发一款小程序管理后台,在众多的php开源后台中,最终选择了基于thinkphp5的,轻量级的thinkadmin系统,进行二次开发。该系统支持php7。文档地址ThinkAdmin-V5...

thinkphp5.0.9预处理导致的sql注入复现与详细分析

复现先搭建thinkphp5.0.9环境...

thinkphp5出现500错误怎么办(thinkphp页面错误)

thinkphp5出现500错误,如下图所示:相关推荐:《ThinkPHP教程》require():open_basedirrestrictionineffect.File(/home/ww...

Thinkphp5.0极速搭建restful风格接口层

下面是基于ThinkPHPV5.0RC4框架,以restful风格完成的新闻查询(get)、新闻增加(post)、新闻修改(put)、新闻删除(delete)等server接口层。1、下载Thin...

基于ThinkPHP5.1.34 LTS开发的快速开发框架DolphinPHP

DophinPHP(海豚PHP)是一个基于ThinkPHP5.1.34LTS开发的一套开源PHP快速开发框架,DophinPHP秉承极简、极速、极致的开发理念,为开发集成了基于数据-角色的权限管理机...

ThinkPHP5.*远程代码执行高危漏洞手工与升级修复解决方法

漏洞描述由于ThinkPHP5框架对控制器名没有进行足够的安全检测,导致在没有开启强制路由的情况下,黑客构造特定的请求,可直接GetWebShell。漏洞评级严重影响版本ThinkPHP5.0系列...

Thinkphp5代码执行学习(thinkphp 教程)

Thinkphp5代码执行学习缓存类RCE版本5.0.0<=ThinkPHP5<=5.0.10Tp框架搭建环境搭建测试payload...

取消回复欢迎 发表评论: