Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

P1: Classic Arcade Game (II) #recall# #141

Open
28 of 30 tasks
EthanLin-TWer opened this issue Mar 14, 2017 · 5 comments
Open
28 of 30 tasks

P1: Classic Arcade Game (II) #recall# #141

EthanLin-TWer opened this issue Mar 14, 2017 · 5 comments
Assignees

Comments

@EthanLin-TWer
Copy link
Owner

EthanLin-TWer commented Mar 14, 2017

Tasking

  • 阅读需求,Tasking 并估时(30min)
  • 使用 ghi 为对应 tasking 创建 issue(10min)
  • 把游戏克隆到本地,并用 IDE 打开(3-5min)
  • 基础设施
  • 让游戏能跑起来
    • 把骨架重构成 ES6 代码(40min)
    • 把代码 bundle 到 dist 目录,且能在浏览器中运行起来(20min)
  • 核心功能实现
    • player 要能上下左右移动(30min)
    • enemy 也要能以恒定速度移动(10min)
    • enemy 速度可调(5min)
    • 能实现碰撞检测(20min)
    • 碰撞发生后 player 要复位(5min)
    • 游戏胜利后 player 也要复位(待确认需求)(5min)
  • 边缘功能实现 - 错误检测
    • player 不能超出画布(15min)
    • enemy 能穿过屏幕,能循环出现(15min)
  • Styleguide
    • 看是否还有可重构的点(30min)
    • 编写 README(30min)
    • 样式对齐(20min)
  • 提交作业(10min)
  • 写博客分享 Sharing: Write a blog again frontend-nanodegree-arcade-game#28
  • 阅读 Code Review 的反馈(40min)
  • 刻意练习 P1: Classic Arcade Game (IV) #dp# 刻意练习 #149
@EthanLin-TWer EthanLin-TWer self-assigned this Mar 14, 2017
@EthanLin-TWer EthanLin-TWer changed the title P1: Classic Arcade Game (II) - In ES6 and Mocha P1: Classic Arcade Game (II) #recall# Mar 18, 2017
@EthanLin-TWer
Copy link
Owner Author

EthanLin-TWer commented Mar 23, 2017

本文首发:https://discussions.youdaxue.com/t/classic-arcade-game-es6-tdd/36499 。欢迎转载,注明作者与出处即可。后续文章更新以 我的 Github Issue #141 为准。文章无法同步更新,请见谅。

项目地址:

文章目录

  1. 回顾 Preface
  2. 极致的开发体验 Superb Developer Experience
    • 触手可及的任务列表管理 GHI + Issues: Tasking List Management in Hand
    • 持续交付的最后一公里 Travis Dashboard: One Last Mile of Continuous Delivery
    • 提交、样式检查自动化 Git Hooks + ESLint: Automated Commit Message & Styling Check
    • 模块化的系统 ES6 Modules: Moduralized Units
    • 小步前进的测试驱动开发 Mocha/Chai/Sinon: Baby Step Test Driven Development
    • 刻意练习,持续进步 Toggl: Measurable Deliberated Practice
  3. 项目完成概览 Overview of Project Accomplishment
  4. 如何使用 TDD 完成代码 TDD in JavaScript
  5. 刻意练习 Deliberate Practice

回顾 Preface

咳咳,上回我们完成了 Udacity 的第一个代码作业,同时也留下了一些痛点没有解决,这篇文章,我们将着重解决这些痛点:

  1. ES5 需要手动实现一些语言层级的特性,比如继承
  2. 没有模块化,导致了无法 TDD
  3. 没有单元测试,从而无法 TDD,无法重构
  4. 没有持续集成流水线,不能提供每一次构建的极速反馈
  5. 没有样式自动化检查,从而样式只能人肉手验

以上几个问题,其实提的都是团队开发规范的问题。在公司的大项目中,由于要协调多人的团队开发(说白了就是对人布朗运动的不信任),我们需要一些约定和规范,比如每个方法代码不能超过多少行、循环不能超过多少层,等。这里,我们更多是使用这些工具 来增强开发体验,把尽可能多的工程活动(流水线、checkstyle、单元测试)都自动化起来,通过它们来提供 快速反馈强化开发信心

以上问题的解决方式分别为:

  • 1和2的解决:使用 ES6
  • 3的解决:引入自动化测试工具 mocha/chai/sinon
  • 4的解决:引入持续集成流水线工具 Travis
  • 5的解决:引入样式自动化检查工具 ESLint

极致的开发体验 Superb Developer Experience

触手可及的任务列表管理 GHI + Issues: Tasking List Management in Hand

这是我新发现的工作流,尚不是很成熟,但令人眼前一亮。它主要解决的问题是更随手可得的 tasking 列表管理。开始每项工作之前,我们一定要分解出一个任务列表,而这个任务列表,你要保持更新,做完了一项要从任务列表中删掉;同时,这个任务列表一定需要更触手可及。前面的文章 是直接编辑 Github Issue,这样既分散精力, Github 也不是一个好的编辑器。如何让 Github issue 更触手可及呢?我想到了命令行。记得之前前端早读课推送过一篇文章,提到 ghi 这个工具,我就一搜,发现这是一个完美的 Github issue 命令行工具。

使用了 ghi + Github issue 来描述任务列表以后,这个工作流就变成:

  1. 先做 tasking,分解出一个任务列表
  2. 把这个任务列表使用 ghi open <issue title> 挨个变成 Github issues
  3. 在命令行使用 ghi list 查看 open 的任务列表,选取一个进行工作
  4. 在提交时,使用对应的 issue number 写入提交信息
  5. 做完一个任务以后,使用 ghi close <issue number> 把任务关闭掉
  6. 重复3-5,直至所有任务做完

image

image

如上,Github 上(或在其他地方)分解出来的任务列表,最后变成 Github issues,被 ghi 工具在命令行管理起来,并且支持 新增 ghi open、查看ghi list、关闭ghi close 这三个简易的管理操作,从而抛弃了 Github issues 的 GUI 界面,使用了终端作为沟通工具(我为命令行设置了一个全局快捷键 Shift+delete 一键打开)提升了效率。

持续交付的最后一公里 Travis Dashboard: One Last Mile of Continuous Delivery

image

image

持续交付(Continuous Delivery)的目标是,每一次提交、构建都是完整可交付的产品代码。实现上,大多 CI/CD 工具都提供了监控界面,这样我们可以快速看到某次是失败还是成功,了解产品的健康状况。为什么我们提倡保持每次提交的代码都是 ready for production 的状态,并且任何时候提交挂了就要马上修复呢?这样一方面可以让我们对产品保持信心,一方面也是让软件开发变得更简单,降低了调试成本。试想,如何一个构建最近20次都是挂的,你怎么知道导致构建失败的问题是什么呢?你怎么知道这20次提交中有无引入新的 bug 呢?你又怎么知道新增的代码有无测试和代码检查的覆盖呢?如果允许一个产品长期是挂球的状态,长此以往必然使开发对产品、对日常开发失去信心,充满沮丧。反之,如果我们保持流水线每次都是绿的,那么即使某一次提交把它挂掉了,我们也能很快找出这个提交、定位问题,很快地修复问题,从而对维护代码的质量起到正反馈的作用。

这里我使用的是 Travis Pipeline,它是对个人免费的产品,并且配置简单,界面相当友好。可以看到上面的提交历史中有一次红掉的提交,这样你很快就可以定位到,是 EthanLin-TWer/frontend-nanodegree-arcade-game#25 的 issue、添加了 ESLint 的 prefer-const 检查规则后挂掉的,那么十有八九就是样式检查没有过,马上改一下,就可以以极小(10秒到1分钟)的成本修正错误。从这个例子也可以看到,好的提交信息的重要性,它描述了代码做的事情(而不是怎么做),让你一眼就能看懂,从而不需要亲自去看提交的代码才能知道,这也降低了调试成本。提交信息,正是我们下一节要提到的点。

提交、样式检查自动化 Git Hooks + ESLint: Automated Commit Message & Styling Check

image

提交信息也是一门小学问。好的提交信息可以简易地代替代码阅读,让你就像在读小说一样读代码库,找 bug 的时候(什么?你问有了上面的持续集成/交付(CI/CD)实践为什么还需要找 bug?这是一个好问题!)也可以通过阅读提交信息来快速定位可能有问题的提交。另外,当团队大了以后,每个人可能有不同的提交信息书写习惯,此时团队间统一提交信息格式就尤为重要。即使是一个人的项目,强制规范提交信息也是有必要的,这不仅有益你养成良好的小步提交习惯(大步提交,提交信息必然无法写好),而且也是程序员的自我修养。

在 Udacity 的项目中,官方也有一份 Git 提交信息样式指南,其中的前缀规则非常有用,我已经用到我的项目上。现在,我自己的提交规则是:

  • 必须有自己的名字
  • 必须有 issue number(有需求就建 issue,没需求就不做卡)
  • 必须有 前缀(feature/refactor/fix/chore/style/docs 其中之一,参考 Git 提交信息样式指南
  • 前缀后面必须有冒号
  • 冒号后面必须有一个空格
  • 空格后面必须小写开头
  • 必须不多于70个字符

比如下面就是一个符合提交规范的提交信息:

[Linesh][#23] Refactor: extract move() method

但是问题来了:你怎么保证你的每次提交都能遵循完全相同的提交格式呢?这不仅要求你对提交规则烂熟于心,而且有时人为的错误(比如打字打错等)更是无法避免的,有没有自动化的方式来辅助检查提交信息呢?当然有。答案就是 Git 原生提供的 Hooks

Git Hooks 是比较大的系统,这里不深讲因为我也只知道冰山一角,但它的思想在软件工程或库开发中都比较常见。因为库或框架可以复用最基本的工作流,而灵活的定制能力则通过提供前后的拦截器或 hook 来允许用户自己扩展,比如 npm scripts、生命周期(比如 Servlet、React Component 等的生命周期概念)等。我们这里的目标是要检查提交信息的格式,如果格式不正确则拒绝该次提交。这里我用到的一个 hook 是 commit-msg,它位于 .git/hooks/ 文件夹下。它正是允许你在提交前后做一些操作的 hook:

#!/bin/sh

commit_regex="\[Linesh\]\[#\d*\] (Chore|Feature|Fix|Docs|Style|Refactor|Test): [a-z]"
error_msg="Aborting commit, please double check your commit message."
commit_msg=$(cat $1)

if ! echo "$commit_msg" | grep -E "$commit_regex" ;
then
        echo "$error_msg" >&2
        exit 1
fi

调试这个脚本可费劲了,说到底还是我的 bash 基本功不扎实,基本是边 stakeoverflow 一边调试的节奏。说是如此,还是遵循小步试错的思想来的,比如一开始我是把 commit_regexcommit_msg 都设成最简单的 Linesh,然后再一边加 []#()等这些特殊符号,看看它们需不需要被转义。并且最初是另外写了个单独的 bash 文件单独运行快速调试的。这样一步一步把 commit_regex 这个正则试出来以后,copy 到 commit-msg 里面发现居然还不 work!最后只得去看官方文档,也才发现 $1 这个参数传进来的是 .git/.COMMIT_MSG 这个容纳了提交信息的文件名,而非提交信息本身,你还必须 commit_msg=$(cat $1) 才能拿到提交信息。总体上说,这是搭建开发环境时比较耗时的一个部分。

image

Udacity Styleguide 里有一条,函数声明后面不要有分号 ;,而其他所有语句包括变量声明等后面都需要分号 ;,怎么一口气把它们全找出来?还是通过一些样式自动检查的工具,比如 JSHintESLint 等工具。自动化起来还有一个好处是,你不需要在大脑中再开一个“进程”来记忆它,也不需要手动来寻找,这样非常耗费宝贵的时间,工具可以自动帮你找出所有不合规范的地方。如果把样式检查一起配置到 CI 上,每次不合规范的提交都会把流水线挂掉变红,你就会第一时间得到通知,马上去修复。如上图,它提示了说有7个地方该加分号没有加(Missing semicolon)。

模块化的系统 ES6 Modules: Moduralized Units

没有模块化是 JS 一直的痛啊,从语言诞生即如此。我们为什么想要模块化呢?因为这是我们管理一个软件系统复杂性的方法,有了它我们可以分别对每个模块进行单元测试。好在 ES6 之后,标准终于提出了一套实现模块化的规范,只不过最新的 NodeJS 还不支持,因此,我们要使用 Babel 等转译器(transformer)来对使用了模块化的代码进行转义。这里我不多啰嗦了。只需要通过 npm 引入 babel-core 和一些语法 preset 即可,同时测试代码也需要被转译。

.babelrc
{
  "presets": [ "es2015", "stage-0" ]
}
package.json
{
  ...
  "scripts": {
    "test": "mocha test --recursive --compilers js:babel-core/register"
  } 
  ...
}

小步前进的测试驱动开发 Mocha/Chai/Sinon: Baby Step Test Driven Development

image

image

有了模块化,有了测试工具,再加上一纸任务列表,我们终于可以进行 TDD 了!简而言之,TDD 是一种测试先行的方式,也即你先写一个测试来描述你的意图,那么测试必然会挂,然后你再通过最快最小的产品代码来实现需求,让测试通过变绿。最后,在测试的保障下,进行必要的重构,消除代码的坏味道。 TDD 是一种设计工具,是一种编码的方法论。它能带来的好处有:

  • 驱使你思考代码的设计
  • 做正确的事:也即只有任务列表的需求才会去写测试和实现
  • 提供极速的反馈:写完一个测试或实现,马上就能看到是挂还是没挂
  • 提供重构的信心:有了测试覆盖和小步前进,重构和设计再也不是艰难的事情,而是愉快的

image

TDD 如何保证你做完了正确的事情呢?换个问法,你怎么知道你做完了 rubric 上声明的所有需求了呢?有同学可能会说,玩一下游戏不就知道了。也没错,不过缺点是需要手动测,并且往后每动代码就必须回归全测一遍,慢。也有同学会说,依据就是前面的任务列表呀,任务列表做完了,我就很确定所有的需求都做完了,因为我的任务列表完整、穷尽地覆盖了 rubric 上所有的需求。很好,思路是对的。我们 tasking 出来的任务列表最后会变成一个个的测试用例,那么,如果所有的测试用例都实现了,同样也证明我的任务列表完全实现了,也就等价于需求完全实现了。测试用例实现没有,这个就非常可视化了,见下图,1秒证明我实现了所有需求,并且自动化的单元测试可以在以后回归的时候重复多次地运行,成本极低。

image

关于 TDD 的深入论述和实践,可以参考上篇提到的一些资料。这是额外的话题,有兴趣深入、了解、质疑的同学欢迎加我微信或群里讨论哈~

刻意练习,持续进步 Toggl: Measurable Deliberated Practice

image

image

image

Toggl 是我使用的一个计时工具。为什么要对任务实现计时呢?如果有同学戳进去了上面👆的那篇编程的精进之法,就会看到作者对刻意练习的观点:通过预估用时 - 实际用时的对比来定位实际耗费过多时间的瓶颈所在。Toggl 可以对整个项目的完成时间做一个记录。当然,类似的计时需求可以通过 IDE 自带的 time tracking 功能来做到,都是可以的。同学们有什么更好的工具也欢迎来分享。

项目完成概览 Overview of Project Accomplishment

Lesson Description Estimated Effort Duration Total
P1 - Arcade Game 阅读项目要求 - -
把游戏克隆到本地,用 IDE 打开 #4 5min 4min 4min
Tasking
任务分解 #2 30min 12min
将任务列表创建为 Github issues #3 20min 7min(spike) + 16min 35min
Infrastructure
持续集成流水线 Travis #5 10min 6min
提交记录 Git Hook #29 50min 2h(120min)
安装 yarn #6 2min 2min
安装 ESLint #7 10min 13min
安装 ES6 转译器 Babel #8 10min 1min
安装 mocha/chai/sinon #9 2min 9min
运行第一个测试 #10 5min 14min
安装 browserify #11 2min 8min
更新项目的 .gitignore #12 2min 1min 2h 54min
Get the Game Up and Running
把骨架重构成 ES6代码 #13 40min 16min
把代码 bundle 到 dist 目录,且能在浏览器中运行起来 #14 20min - 16min
Core Features
player 要能上下左右移动 #15 30min 85min
enemy 也要能以恒定速度移动 #16 10min 5min
enemy 速度可调 #17 5min 14min
能实现碰撞检测 #18 20min 31min
碰撞发生后 player 要复位 #19 5min -
游戏胜利后 player 也要复位 #20 5min 19min 2h 39min
Other Features - Error Handling
player 不能超出画布 #21 15min 15min
enemy 能穿过屏幕,能循环出现 #22 15min 12min 27min
Styleguide
看是否还有可重构的点 #23 30min 25min
编写 README #24 30min 1min
样式对齐 #25 20min 37min 53min
8h

如何使用 TDD 完成代码 TDD in JavaScript

突然觉得这部分没什么好说的,TDD 怎么做就是怎么做,说了似乎就变成纯 TDD 贴了。有同学可以给点建议写什么吗?或者,有兴趣的同学可以看一下我的 PR 和提交历史,非常欢迎你的反馈!

EthanLin-TWer/frontend-nanodegree-arcade-game#30

刻意练习 Deliberate Practice

  • 发现一个问题:使用了 TDD 实现核心功能使用了209min,不使用 TDD 实现核心功能使用了235min,感觉差别似乎不是特别大,而且后面实现是在已有前面理解的基础上。不过 TDD 从心理上,感觉对项目的信心倍增,无论是功能还是重构,主要还是样式检测和测试自动化在背后的支撑
  • 基础设施的设置则需要更多的时间,差不多需要3个小时。可以看到主要是在 Git Hooks 的配置上花费的时间,背后又是对 bash 的不熟悉
  • Tasking 时间减少了,而且 task 的总体质量还不错。大🐻说,这个例子的逻辑相对简单,建议可以多做些逻辑更复杂的任务分解练习
  • 把骨架代码重构成 ES6 意外顺利
  • #9 #10 #11 #17 #18 #25 花费的时间比预期多了,需要看下提交历史,看是意外的 debug,还是有没预料到的任务
  • Git hooks 上花费的大量时间本质上是 bash 不熟悉的原因
  • 对比一下项目功能完成与重构时间:上次训练,完成项目代码 115min,重构 98min,总用时 213min;这一次,完成项目核心功能 186min,重构 25min,总用时 211min,并没有太大变化。但反映出一个问题:对重构的不熟练,及对 JavaScript 重构的不熟练。可以产出刻意练习计划

下次目标:核心功能总用时进3小时;基础设施代码搭建进1个半小时

刻意练习计划:

  • 特定的重构手法
  • 基础设施 和 核心代码构建 分开训练

@JimmyLv
Copy link

JimmyLv commented Mar 24, 2017

卧槽,太可怕了!

@JimmyLv
Copy link

JimmyLv commented Mar 24, 2017

写这个 Issue 花了多久?😂

@EthanLin-TWer
Copy link
Owner Author

哈哈哈哈哈,第一篇博客5个半小时,第二篇4个半小时…闲得蛋疼!

@JimmyLv
Copy link

JimmyLv commented Mar 28, 2017

可怕!

@EthanLin-TWer EthanLin-TWer reopened this Aug 19, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants