程序员软技能

上篇:

工具、效率、时间管理

引:
要成为一名专业人士,需要养成的另外一个强大的习惯就是时间管理技能

一、工具(软件开发工程师)

好用的工具可以在开发中处理一些事情上达到事半功倍的效果

  • 编译器:IntelliJ IDEA、Eclipse(老牌工具可以淘汰了)日常写代码

  • 终端:ITerm2 + oh My zsh, Mac下好用的终端,自带一系列常用 alias、选中即复制、强大的bash等等,完成日常一些终端操作(提交代码、mvn编译、ssh登陆(可配置一键登录或alias)等等)

  • 浏览器:chrome+一系列方便的插件(Vimium、Adblock、ESHead、划词翻译、掘金、Tampermonkey等等)着重推荐Vimium 基本操作浏览器可以不用鼠标了,习惯后效率肯定高于鼠标操作。

  • 本地/远程调试:postman

  • 数据库管理工具:Sequel Pro, mac下快捷键切换比较方便

  • 任务管理TickTick:滴答清单(自带番茄时钟和todoList)

  • git一些好用的界面工具:GitHub Desktop(方便看diff避免误提交)+Sourcetree(方便看git log和搜索)git命令都可以做到但展示效果没有界面工具效果好。

  • 终极神器:Alfred,可以完成mac下一系列的骚操作. 比如快速切换上面一堆应用、一键执行mac上任何一个任务及打开某个功能、搜索某个东西、比如定制自己的workflow基于脚本一键完成线上token获取等等等。

以上,串起来上面的工具,代码写到提交完整过程可以是:从自己todo管理工具里找到任务—->idea写代码—->打开github desktop对比本次git diff差异-→item2 mvn自编译通过再提交。

git相关常用操作(因此强烈建议配置alias)

  • 推送拉取合并:git pull、git rebase、git merge、git push -f origin/xx

  • 本地常用commit点操作:git checkout xxx 、git checkout -b xxx、git amend 、git rebase -i

  • 多任务协同开发:git stash 或每个任务单独切分支,每次push前pull 并rebase下远程分支避免分支和提交的path冲突混乱,合入后再git branch -D 删掉

  • 回退恢复:git reset –hard/soft (hash)

  • 操作提交的commit远程分支上线前用得比较多:git revert/git cherry pick等

二、效率&时间管理

即一些工作意识

早晨——一天的开始:比如早上打开电脑第一步:check 当天邮件会议&日程安排、番茄(活动清单->今日待办—>估算番茄数(25分钟/个)—>执行+反馈记录) 请使用至少2星期,然后好习惯的话,若有比较重要线上业务监控,可以看看监控图、每天要解的bug、查收邮件可以固定放在某一定点,在工作中的番茄时间里尽量不被外界因素打破(会议),定时看看提交的patch列表在里面找几个评审review下。

除了TickTick(滴答清单)工具外,也可使用名为Trello(http://trello.com)、Kanbanflow(http://simpleprogrammer.com/ss-kanbanflow)的工具作为看板来组织一周的工作,推荐后者,因为Kanbanflow有一个内置的番茄钟定时器。

晚上——一天的结束:晚上固定时间回顾跟踪当天清单里task完成情况,基于番茄时钟时间追踪,了解分析每天的时间都是怎么花掉的,看看数据,找出最大的2~3个时间杀手,并及时调整必要的话反思或反馈等等。
关于会议:符合精简原则,关于精简会议的更多细节,可以参考Jason Fried和David Heinemeier Hansson合著的《重来》(Rewor)[Crown Publishing Group, 2010]。豆瓣
学会分解任务——要吃掉一头大象,每次吃一口
关于复盘:复盘的意义是什么,我们为什么要复盘?推荐一部电影《萨利机长》
排期&管理:推荐一篇文章,看看NASA航天级别超级大项目是如何被高效管理的 “阿波罗”登月中的工程管理一瞥
一些常见的时间杀手
看电视。
社交媒体。
新闻网站。
不必要的会议。
烹饪。
玩电子游戏(尤其是网络游戏)。
工间喝咖啡休息。

三、番茄工作法介绍

番茄工作法介绍
”对番茄工作法的正确理解可以令工作生活大为改观,它不仅能帮我能做更多事情,而且能让我可以尽情享受业余时间。一旦我完成了当天的目标(以番茄钟来度量的),我就可以自由自在地做自己想做的事情,甚至能事先控制自己将时间用在哪儿,而不是回过头看自己的花时间都去哪儿了。“ ——某书摘

数学家、理论物理学家、工程师和科学哲学家昂利·庞加莱(Henri Poincaré)在一篇发表的文章http://www.calnewport.com/blog/?s=%23craftsmanincubicle中写到: “加莱……通常在上午10点到12点和下午5点到7点工作。他发现工作再长时间也鲜有成果。”
John Resig的一篇博客文章,John是我非常尊敬的开发者。他在一篇题为“每天写代码”(Write Code Every Day)的文章中,谈到了自己的经历。他之前在业余项目上毫无进展,直到养成了每天至少用30分钟写一定量有用的代码的习惯。在实行新惯例之后,它成了一种习惯,这使他的生产 力获得了巨大的提高。
可以在http://simpleprogrammer.com/ss-write-code阅读这篇文章的完整内容。
番茄工作法介绍:一些番茄工作法有关的文章https://monotasking.blog/

四、相关书籍

比较有现实指导意义的书籍

《软技能-代码之外的生存指南》https://book.douban.com/subject/26835090/

《番茄工作法图解》https://book.douban.com/subject/5916234/

《搞定-无压工作的艺术》https://book.douban.com/subject/4849382/

长期来看值得思考

《原则-生活和工作》https://book.douban.com/subject/30189843/

《暗时间》 https://book.douban.com/subject/6709809/

《奇特的一生》https://book.douban.com/subject/1115353/

下篇 :

开发规范、迭代管理规范

一、迭代管理规范

1.1迭代节奏

image-20220418221210621

1.2、 Scrum 实践

image-20220418223155864

​ 1⃣️-9⃣️是一个敏捷迭代的过程

在阶段2⃣️的sprint Planning Meeting 中应该产生模块负责人,自愿方式(产生自RD、FE、PM)中。
模块负责人职责:

    a、迭代周期中定期check所负责模块干系人员开发进度,同步风险。
    b、协调资源,保障所负责模块顺利上线
    c、参与日常站会

在阶段3⃣️已经确定下了本期sprint Backlog后,接着还应该再做两件事:

a)分派

​ 由负责人拆分大的需求卡片(可以是迭代需求和系统优化需求2部分),然后由开发同学主动认领或由负责人指派任务卡片。

​ 一个拆分后的icafe卡片应该至少包含:

​ a、任务标题及类型
​ b、必要时的任务内容概述
​ c、负责人
​ d、工时估算

b)协调排期

​ 往往一个需求在sprint期间需要PM、RD、FE、QA一起协作完成,因此在sprint的开始阶段4⃣️开始前,应该事先协调好不同角色参与者的排期。

具体方式可以是一个可以直观的展示时间轴的排期日历或看板墙,比如云端共享excel或目前采用的甘特图软件,方便sprint期间聚焦对齐,模块负责人也方便及时跟进。

image-20220418223920020

排期协作带来的好处:

​ 模块负责人机制保障了分工明确,上下对齐,很好的锻炼了个人协调能力,帮项目总负责人(scrumMaster)分担精力
不同角色参与者事先明确排期,最大化的规避了delay风险,提高了彼此的效率和团队协同节奏感

二、后端编程规范

大部分上规则参考阿里巴巴Java开发手册(华山版)

开发中常见一些容易忽视的开发习惯整理:

1、命名问题:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
1.1、局部变量
反例
{
//过于随意的局部变量命名,不够整洁和严谨
User rs = userService.getUserInfoById(userId);
String m = rs.getMobile();
String id = rs.getIdCard();
String add = rs.getAddress();
String v = rs.getValue();
}

正例
{
User user = userService.getUserInfoById(userId);
String mobile = user.getMobile();
String idCard = user.getIdCard();
String address = user.getAddress();
}

1.2、私有方法命名

私有方法有时候和会和当前所在类关系不太大,仅是临时用于抽取,故应该尽量在命名上能见名知义
反例:
Map getInfo(){
}
Map getMap(){
}

2、循环嵌入较深

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
反例,嵌入过深,降低了代码可读性和可维护性
User rs = userService.getUserInfoById(userId);
if (rs != null) {
Order rs2 = orderService.getOrderInfo(orderId);
if (rs2 != null) {
String name1 = rs2.getOrderName();
if (name1 != null) {
///
}
}
}


正例
//在一开始就取反返回,有效避免了后续更多的嵌入,或借助Optional消除分支判断
User user = userService.getUserInfoById(userId);
if (user == null) {
return false;
}
Goods goods = stockService.getGoodsInfoById(goodsId);
if (goods == null) {
return false;
}

3、service做的逻辑过于冗余

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
反例,12-25行做的逻辑全部是check参数和适配组装等常见的service层无关的次要逻辑,显得整个isEnableToBy方法逻辑略显臃肿
//判断当前用户是否可以支付当该订单
@Autowired
private UserService userService;
@Autowired
private StockService stockService;
@Autowired
private MemberService memberService;
boolean isEnableToPay(String userId, String orderId) throws IOException {
User user = userService.getUserInfoById(userId);
if (user == null) {
return false;
}
int age = user.getAge();
String mobile = user.getMobile();
String idCard = user.getIdCard();
String address = user.getAddress();
if (mobile != null && mobile.length() > 11) {
throw new RuntimeException("电话格式非法");
}
if (age < 18) {
throw new RuntimeException("年龄非法");
}
UserDto userDto = new UserDto(user);
boolean isMember = memberService.isMember(userDto);
if(!isMember){
return false;
}
boolean isOrderExist = orderService.isExist(orderId);
if (!isOrderExist) {
return false;
}
//step3:checkOrder
boolean isOrderExist = orderService.isExist(orderId);
if (!isOrderExist) {
return false;
}
return true;
}

正例:尽量简化service逻辑,主干只保留了引入的service参与计算的逻辑,次要非service逻辑,抽离出去,显得过程会清晰,例子中isEnableToPay方法只干了3件事,a)获取用户信息、b)检测是否是会员、c)订单是否存在,完成了判断用户是否可以完成支付这件事,思路也比较清晰


//判断当前用户是否可以支付该订单
boolean isEnableToPay(String userId, String orderId) throws IOException {
//step1:get userInfo
User user = userService.getUserInfoById(userId);
boolean legalUser = UserUtil.checkUser(user);
if (!legalUser) {
return false;
}
//换行
UserDto userDto = UserUtil.adapterUserDto(user);
//step2:check isMember
boolean isMember = memberService.isMember(userDto);
if (!isMember) {
return false;
}
//step3:checkOrder
boolean isOrderExist = orderService.isExist(orderId);
if (!isOrderExist) {
return false;
}

return true;

}

4、类的职责问题

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
//4.1 XXXDao、XXXService
反例:订单服务接口定义中又出现了查询库存服务,势必service实现类又被迫引入了OrderDao,显得服务职责不够明确,后拆分期维护成本大
public interface OrderService {
//有时 类名、方法名、参数、返回值 4者一起,可一起用来声明该方法
boolean isExist(String orderId);

Order getOrderById(String orderId);

Stock getStockInfo(String stockId);

}
正例:基础的Dao、Service层只做当前XXXDao、XXXService相关的CURD逻辑,禁止混用,复杂的业务可以再向上抽象一层service调度各基础层service做交互计算,最终在controller层统一收口

public interface OrderService {
boolean isExist(String orderId);

Order getOrderById(String orderId);

}
//将库存服务拆出去
public interface StockService {
boolean isExist(String orderId);

Stock getStockInfo(String stockId);

}
//4.2 尽量不要在controller层做多余的逻辑(可以封装到serveice层),controller层做到足够精简,另外bean、XXUtil里不应该引入service.

5、bean的定义

业务上除了解析反序列化提取上游响应信息如:组件es、redis等复杂数据结构外,应该避免直接使用map包装大json,序列化VO或组装DTO时,应该定义好该数据结构。

若不需要反序列化的Response json且创建的频率是小而频繁的,可以考虑使用 #common.model.LiteModel

6、log、注释 规范

1
2
3
4
5
6
7
8
9
10
11
12
1、log
反例:
log.info("success");
正例:关键地方点,日志打印出当前上下文相关的信息,userId,sessionId等有价值的标示信息,否则grep日志排查比较困难,相当于没打日志
log.info("pay success,userId:{},orderId:{},userId,orderId);

2、注释
代码当中出现注释来解释代码,出现注释可能原因是作者认为某些内容没有说清楚,需要增加注释。有些注释比较有用:
之处为什么要使用特定的方式完成某个工作
引入了不常见的算法

其他啰嗦的代码注释可以通过代码来自解释

三、codeReview规范

3.1 面向提交者

  1. 建议先 mvn clean compile -P dev -Dmaven.test.skip=false 保证编译可通过,且已自测
  2. 一次提交的代码量不要太大
  3. 一个评审的变更应该尽量小,只做一件事,如果一件事可以拆分成多件,那么应该对应为不同的cr,例如:尽量不要将【修复一个bug】和【重构一个函数】放在同一个评审里
  4. 精准指定reviewer (必须指定:1、自己当前负责开发模块相关负责人,2、小组所在具有+2权限的同学(blocking reviewer),3、及一起开发同一模块的小伙伴)
  5. 写好 commit message:见:【四、git commit message规范】
  6. 对于复杂大的新需求或重构,最好能在评审里附上需求背景及技术设计文档,方便评审者快速深入了解背景

3.2 面向评审者

  1. 小组的blocking reviewer、模块负责人及时看icode评审提醒, 必须review代码,并给出结论(打分)

  2. 其余相关人员鼓励踊跃附上精彩评论,blocking reviewer可以作为参考,例如非blocking reviewer有2个+1,则blocking reviewer直接可以给出+2

  3. CodeReview CheckList:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    命名规范,是否见名知义?
    类、函数是否太长,嵌套是否太深
    代码是否具有较高的可读性,再过一年自己还能看懂吗?
    对象NPE判断了吗、各种边界条件处理了吗?
    API设计是否符合接口规范
    代码是否明显存在可复用的地方?
    线程安全、性能问题、是否为良好的设计模式、是否会有内存泄露?
    事务一致性处理,依赖服务宕机时会发生什么?
    自己遇到过同样的坑?
    有testcase?
    业务逻辑处理是否有明显的问题?
    更多见【二、后端编程规范】
    ...
  1. 最佳实践:

    1
    2
    3
    4
    5
    一次review代码在500行内,且不超过30分钟
    及时review代码,确保当天的review任务当天完成,提升彼此效率
    认真review,并对review结果负责
    不要吝惜自己的comments
    整个评审如果都看不懂,可以当面去问

反例:
一块模块T由A1、A2一起开发,A0 是blocking reviewer,但由于A0没有及时review或没有仔细review就 +2合入,同时也会间接造成A1不断的基于模块T提交bugFix,或A1可能后期就直接找不相干的B去合入。

造成的影响是:有始无终,A0 没有能及时知晓且cover住A1代码可能会出现的问题,同时A2也完全不知道后期的变更,是一个尴尬的场景。

正例:

若B是blocking reviewer可以选择拒绝+2(特殊情况除外),A1、A2、A0应有始有终,也减少了A1返工fixBug甚至是面临方案重新设计的风险.

3.3 review带来的好处

  1. 提高代码质量
  2. 找出潜在bug,提高提测质量,避免来回返工
  3. 互相学习,提升团队coding水平
  4. 让其他不熟悉代码的人明白作者意图想法,便于以后轻松维护代码,必要时可backup

四、git commit message规范

commit message由icafe项目标识+【commit类型】+commitDescription组成.

常用到的commit类型:

  • story(产品需求)
  • bugFix
  • refactor(重构&优化)
  • Test(单元测试)
  • Other(不在上述范围内的) 等

commitDescription原则:

  • 尽量简短的一句话描述commit作用
  • 最好使用中文
  • 能体现出主、谓、宾、定若有依赖关系,需要特殊体现

带来的好处:

  • 节省上线发布人员时间,可以清晰的判断变更带来的影响及回滚止损等
  • 规范后可以自动生成changLog也方便统计
  • 反例:
    
    * | | 9dffee08d NextGenDialog-2677 fix bugs
    * | | 5290ce960  Merge "NextGenDialog-2677 fix bugs" into feature-5.3
    * | | 8323b4aae NextGenDialog-2679 fix
    * | | 9fe529f2f NextGenDialog-2039 降低复杂度    //既像bug又像story
    * | | a89f20243 AICP-WEB-3058 优化
    
    
    正例:
    
    * | | 9dffee08d NextGenDialog-2677【bugfix】创建订单时未判断用户是否是会员
    * | | 5290ce960  Merge "NextGenDialog-2677 【bugfix】创建订单时未判断用户是否是会员" into feature-5.3
    * | | 9fe529f2f NextGenDialog-2039 【story】创建订单接口
    * | | a89f20243 AICP-WEB-3058 【refactor】订单创建逻辑优化重构