本周,我的工作模式正式从远程办公回到了现场办公。恰逢过年,整体工作节奏放缓,切换的过程很顺畅。
虽然我十分期待远程办公成为常态,但不得不承认,这种模式在中国落地,还有一段路要走。
Go技巧 - 提高ORM使用体验的三个要点
ORM是一个非常高频使用的开发工具。以下图为例,Go程序内与MySQL中,数据存储是异构的 ,这就导致传统开发方式会分成两步:
- 将Go程序中的数据转换成MySQL的
SQL
语句 - 解析MySQL 返回的数据到具体结构体中
这部分的开发有大量重复性的代码,如拼接SQL、数据解析,所以就有了ORM这个概念 - 将内存中的数据结构(对象)与数据库中的表对应起来。一旦映射关系建立,那就可以调用ORM里的CRUD完成日常开发。在Go语言程序中,最常见的就是gorm。
示例
我们以Book作为对象为例,它在Go程序中的定义是:
1 | type Book struct { |
对应MySQL中的建表语句为:
1 | CREATE TABLE `books` |
完成定义后,我们可以使用gorm
库实现CRUD了。但基于ORM库,开发中还是会高频出现一些奇怪的问题:明明程序没有bug,ORM的操作结果却没有达到预期。例如插入时status
字段是0,没有报错,但查询时缺变成了100。
这类问题,往往是开发者在设计时没有注重 用户认知 导致的,也就是说 现象反直觉、所见非所得。我们今天的话题,将基于此展开:
要点一:程序侧 - 节制地使用ORM能力
ORM往往扩展了很多能力,但大幅度地增加了用户的学习成本与排查问题时的成本。以GORM字段权限控制为例:
1 | type User struct { |
看起来特性很酷,但如果你作为读代码的人,你愿意去读一个结构体中每个Field的tag
详情吗?而且,这种限制藏得很隐蔽,发生问题后排查起来很累。
因此,程序侧的ORM定义,最重要的是能保证程序数据结构与数据库存储结构一一映射,其余特性需要慎用。
慎用不代表不用。
如果能在团队内部形成规范,一方面这个规范能落地到代码里,另一方面也能宣传到各个成员、让大家形成共识,那就能用这些特性提升开发效率。
要点二:数据库侧 - 最简化设计
程序侧的代码对开发者可见,排查问题相对清晰。而如果问题最终是在数据库侧导致的,那么就变得复杂了:
- 技术领域不同 - 数据库存在一定的专业性,经验尚浅的开发者需要一定的经验积累
- 访问权限 - 角色、环境等问题,可能导致排查困难
- 滞后性 - 出现问题的优先排查对象往往是代码,数据库往往会被我们“默认”认为没问题
所以,我们在前期设计数据库侧的内容时,要尽可能地保证简单。我个人的评判标准是:让Go结构体的数据,和MySQL表中的一行数据完全对应,不做额外的工作。
我举两个反例:
- 字段默认值有特殊的含义,如建表时
status
的默认值设置为100- 改进方案:如果100这个值有业务含义,应在Go程序中设置
- 表中增加Trigger,如
status
字段修改为某个值后,自动触发另一个字段的修改- 改进方案:在Go程序中实现这块逻辑
要点三:ORM能力与数据库特性的综合考量
第三个要点最为复杂,它需要结合ORM库的具体能力以及数据库的自身特性来综合考量:ORM的有些特性并不完善,具体在哪实现?
依旧以gorm为例,在用Book
结构体进行多列更新时,无法更新其中的默认值,如
1 | // 官方示例 |
我们先不考虑具体解决方案,而是希望大家能认识到ORM的局限性 - 想用一个结构体完全覆盖所有的增删改查场景,是不现实的。选择方案,其实是trade-off
,选择一个团队更能快速理解的策略。
想了解方案的同学,可以参考我之前的博客。
第三点是进阶性质的能力,需要大量ORM与数据库侧的开发经验,今天不作展开。
小结
ORM的使用体验会大幅提升CRUD的开发与维护效率。我比较提倡 在设计时,最简化ORM与数据库侧的特性,只采用其核心的映射能力。
而当简化到一定程度后,我们可以打通两侧的数据结构,如示例中的Book
结构体与books
建表语句。由于MySQL中的数据类型更为复杂,可以维护一个从 解析建表语句,自动生成Go中ORM结构体 的代码生成工具。
实现可以参考博客
编程思考 - 开发者的coding经验
如今的应届生在校或实习时就具备了颇为深厚的编程经验,参加工作后能快速地胜任日常需求,这就引起了老一批工程师的焦虑,不禁怀疑:我们的coding经验究竟有什么价值?
下面,我分享一下个人的思考,会从低到高三个维度进行讲述:
- 代码维度 - 写得好,读得懂:看过、写过的代码多,一方面让自己写代码时可读性提高,另一方面也能适应五花八门的项目风格。
- 功能维度 - 懂需求,善取舍:代码所实现的功能,往往和最终预期有出入,如沟通损耗、认知差异等;而功能实现的过程中往往需要取舍,要理清主次先后。
- 系统维度 - 识风险,促迭代:开发的代码从来就不是孤立的,需要识别出它对系统其余功能是否会产生风险;同时,本次开发也是一个迭代的机会,例如建设更通用的模块、修复一些历史包袱等。
以上三点维度不同,但很难从价值维度区分高低。从这三点来看,一个资深coder对团队的价值非常重要。
工作生活 - 焦虑感的缓解
这几年,我的焦虑感与日俱增,尤其是近两年的行业低谷。面对焦虑,专家们有很多思路,这里分享三个对我帮助最大的方法:
- 多锻炼,既能保证身体能量充沛,又可以释放很多负能量
- 多读书(尤其是心理学),提升心智成熟,坦然地面对不确定性
- 多沟通,与同事、领导、朋友等多种角色,进行真诚的交流
Github: https://github.com/Junedayday/code_reading
Blog: http://junes.tech/
Bilibili: https://space.bilibili.com/293775192
公众号: golangcoding