VS Code 编辑器配置

推荐的插件

  • ESLint: 代码规范
  • Prettier: 代码美化
  • EditorConfig for VSCode: 配置编辑器空格、换行符等规则
  • Conventional Commits: 提交信息规范插件
  • GitLens: Git 历史修改插件
  • vscode-icons: 文件类型图标
  • Code Runner:运行代码文件
  • Markdown All in One:markdown插件
  • Settings Sync:通过github gist在多终端同步vscode配置

配置

推荐配置:

  • 字体: Powerline
  • 自动保存
  • Tab Size:2
  • WordWrap
  • Minimap
  • codeActionsOnSave:配置 ESLint 保存时修复
  • Default Formatter: Prettier

配置文件参考:

{"editor.fontFamily": "'source code pro for powerline',Menlo, Monaco, 'Courier New', monospace","editor.wordWrap": "on","terminal.integrated.fontSize": 12,"terminal.integrated.shell.osx": "zsh","terminal.external.osxExec": "iTerm.app","editor.tabSize": 2,"editor.insertSpaces": true,"editor.formatOnType": true,"editor.formatOnSave": true,"editor.defaultFormatter": "esbenp.prettier-vscode","editor.minimap.enabled": true,"files.autoSave": "afterDelay","files.eol": "\n","eslint.validate": ["javascript","javascriptreact","typescript","typescriptreact","vue","html"],"typescript.validate.enable": false,"javascript.validate.enable": false,"javascript.format.enable": false,"javascript.preferences.quoteStyle": "single","prettier.jsxSingleQuote": true,"prettier.singleQuote": true,"prettier.trailingComma": "none","prettier.semi": true,"prettier.tabWidth": 2,"prettier.requireConfig": true,"editor.codeActionsOnSave": {"source.fixAll.eslint": true},"workbench.iconTheme": "vscode-icons","files.exclude": {"**/dist": false,"**/node_modules": false},"git.enableCommitSigning": true,// "typescript.tsdk": "node_modules/typescript/lib","editor.fontSize": 13,"window.zoomLevel": 1
}

其他 IDE 可以参阅: https://leader.js.cool/basic/resource/ide

Git

  • 基础教程: https://git-scm.com/book/zh/v2
  • 通过 SSH 方式连接: https://docs.github.com/cn/github/authenticating-to-github/connecting-to-github-with-ssh
  • 使用 GPG 签名: https://docs.github.com/cn/github/authenticating-to-github/managing-commit-signature-verification

建议:

  • 不要通过 HTTP 方式去 Clone 和 Push 代码
  • 服务器运维中,一律使用 Deploy Key + SSH 方式更新代码
  • 建议使用 GPG 签名验证

常用配置

# 默认分支设为 main
git config --global init.defaultBranch main
# 设置提交用户名
git config --global name "Willin Wang"
# 设置提交邮箱
git config --global email "i@sh.gg"

终端配置

  • zsh + ohMyZsh, 参考: https://leader.js.cool/basic/resource/zsh
  • Mac 终端软件使用 iTerm, 全系统可使用 Hyper.is

Docker基础

如何像盖房子一样写代码

原文: https://leader.js.cool/experience/advanced/coding-as-building

当我写一个功能模块方法时,我在想些什么

// 无论什么方法,都是这样一个结构
const fn = () => {};

比如,我要写一个接口,查询组织下的设备列表 /api/device/list

地基

const deviceList = (params) => {// 传入一些参数return []; // 返回一个列表
};

我需要哪些参数:

  • 用户基本信息(主要是用户 id,用户的组织 id)
  • 用户对应的组织基本信息(主要是组织 id,组织管理员 id,层级关系,以及权限逻辑)

输出结果很简单,为一个数组。

浇筑

第一步分析,存在成功和错误(错误类型先不考虑)两种类型的结果。

// 成功
// 错误
const deviceList = async (ctx) => {// 错误if (someError) {// 返回错误结果}// 成功return getDevicesByOid(oid);
};

这是一个大概的设想,没有必要将代码写出来。然后润化该思路,写出第一段框架。

主体结构

首先,传入的参数为组织 oid,用户的信息可以通过 session(或其他方式)从内部获得。

可能的一种思路

// 成功
// 错误
// 错误1:用户未加入组织
// 错误2:传入参数组织不存在
// 错误3:用户无组织权限// 传入参数: 要查询的组织 oid
// 能够通过 session 取到的信息: user
const deviceList = async (ctx) => {// 用户信息 ctx.user// 判断用户是否有组织if (ctx.user.oid === 0) {// 错误1:用户未加入组织}// 如果不传该参数,查询当前用户组织的设备const { oid = ctx.user.oid } = ctx.request.body;if (oid === ctx.user.oid) {// 成功return getDevicesByOid(oid);}// 根据oid查询组织信息// 错误2:传入参数组织不存在// 判断是否有权限const checkRights = await checkUserOrgRights(ctx.user.uid, oid);if (!checkRights) {// 错误3:用户无组织权限}// 成功return getDevicesByOid(oid);
};

推荐的实现方式

// 成功
// 错误
// 错误1:用户未加入组织
// 错误2:传入参数组织不存在
// 错误3:用户无组织权限// 传入参数: 要查询的组织 oid
// 能够通过 session 取到的信息: user
const deviceList = async (ctx) => {// 用户信息 ctx.user// 判断用户是否有组织if (ctx.user.oid === 0) {// 错误1:用户未加入组织}// 如果不传该参数,查询当前用户组织的设备const { oid = ctx.user.oid } = ctx.request.body;if (oid !== ctx.user.oid) {// 为什么这里不用等于判断:如果等于的话,则当时就需要返回出去,这样的话该方法会有两个成功的 return// 根据oid查询组织信息// 错误2:传入参数组织不存在// 判断是否有权限const checkRights = await checkUserOrgRights(ctx.user.uid, oid);if (!checkRights) {// 错误3:用户无组织权限}}// 成功return getDevicesByOid(oid);
};

封顶

完成其他的业务代码。

当我写一段测试的时候,我在想些什么

按照上面推荐方式完成代码后,需要进行代码的测试。

首先需要明确业务的流程,理清测试的思路。

  • 成功

  • 错误

    • 错误 1:用户未加入组织
    • 错误 2:传入参数组织不存在
    • 错误 3:用户无组织权限

主要有两种设计思路:

设计思路

思路一

  1. 完成测试用例,覆盖成功的所有情况
  2. 完成测试用例,覆盖错误 1 的所有情况
  3. 完成测试用例,覆盖错误 2 的所有情况
  4. 完成测试用例,覆盖错误 3 的所有情况

这是传统的单元测试衍生而来的 BDD 测试方式。

这里测试用例的个数应该为8次:

  • 成功:

    • 1.当前组织的用户有传入组织 oid
    • 2.当前组织的用户未传入组织 oid
    • 3-5.上级组织,上上级组织,根级组织的管理员用户传入组织 oid
  • 6.失败 1:用户未加入组织

  • 7.失败 2:传入参数组织不存在

  • 8.失败 3:用户无组织权限

其中,测试 3-5 可以优化为一次测试(即根据所有管理员 uid 的数组比较是否包含当前用户 uid),最终优化后的结果应当为6次。

但由于该思路中不明确用户,所以用户行为无法准确表达,在创建测试数据的时候较为困难,不仔细思考分析,无法优化需要创建多少条测试数据。

思路二

而实际上 BDD 测试为用户行为测试,可以以几类用户的情形分别进行测试。

  1. 模拟一个用户的数据,覆盖成功和可能错误(有可能无法涵盖到所有错误)的所有情况
  2. 根据未覆盖的部分,再模拟另一个用户的数据,覆盖成功和可能错误(有可能无法涵盖到所有错误)的所有情况

以此循环,直至覆盖所有。

  • 用户 1(非组织管理员,查询自己的组织)

    • 1.成功(未传入组织 oid)(组织 1)
    • 2.成功(传入组织 oid)
    • 3.失败 2:传入参数组织不存在
    • 4.失败 3:用户无组织权限(组织 2)
  • 用户 2(上级某组织管理员)(组织 3)

    • 5.成功
  • 用户 3(未加入组织用户)

    • 6.失败 1:用户未加入组织

非常简洁明了的关系,需要 3 个测试用户,3 个组织(上下级关系进行数据复用,一个无权限的组织),即可涵盖所有范围。

最终优化版设计:

  • 用户 1(某组织管理员,有下级组织)

    • 1.成功(未传入组织 oid,查询自己的组织)
    • 2.成功(传入当前的组织 oid(组织 1))
    • 3.成功(传入下级的组织 oid(组织 2))
    • 4.失败 2:传入参数组织不存在
    • 5.失败 3:用户无组织权限
  • 用户 2(未加入组织用户)

    • 6.失败 1:用户未加入组织(组织 3)

两个用户,三个组织。完成所有覆盖。

当我以测试驱动开发的时候,我在想些什么

可以从上述测试思路二中进行反推。

实际上思路可能是在写代码或者写测试的过程中不断的改进和完善的。

  • 如果已经写好了测试正在写代码,可以及时回过头来调整测试;
  • 如果功能写好了又再重新测试,可以在测试优化后再去看逻辑代码是否还有优化的空间。

编写测试代码入门

原文链接: https://leader.js.cool/basic/node/test

谁开发,谁测试。

注意: 原则上应该先写测试,再进行编码;如果需求时间紧,可以先进行功能实现,但务必后续维护时候将测试代码补充完善。

BDD(优先)+TDD(完全代码覆盖)

测试框架

  • ES5: mocha + istanbul
  • ES6: ava + nyc
  • Typescript: jest+ istanbul

TDD

Test Driven Development,(单元)测试驱动开发。

特点:

  1. 直接引用对应源码,执行方法进行测试;
  2. 测试用例须设计完整,把所有分支都 Cover 到。

示例:

describe('Lib Common', function () {'use strict';it('isEmpty', function () {// isObjectisEmpty({}).should.be.equal(true);isEmpty([]).should.be.equal(true);isEmpty({ a: 1 }).should.be.equal(false);isEmpty([1, 2]).should.be.equal(false);// isStringisEmpty('').should.be.equal(true);isEmpty('sth').should.be.equal(false);// isNumberisEmpty(0).should.be.equal(true);isEmpty(0.1).should.be.equal(false);// null and undefinedisEmpty(null).should.be.equal(true);isEmpty(undefined).should.be.equal(true);// booleanisEmpty(false).should.be.equal(true);isEmpty(true).should.be.equal(false);// 最后一行falseisEmpty(isEmpty).should.be.equal(false);});it('md5/sha1', function () {md5('sth').should.equal('7c8db9682ee40fd2f3e5d9e71034b717');sha1('sth').should.equal('dec981e3bbb165d021029c42291faf06f59827c1');});it('authcode', function () {authcode(authcode('test'), 'DECODE').should.be.equal('test');authcode(authcode('test', 'ENCODE', 'key'), 'DECODE', 'key').should.be.equal('test');authcode('c008AsZqmGL8VuEVpZKVlbPwXzSsCZ+YX5K5CAGpMMqn', 'DECODE').should.be.equal('');});
});

BDD

Behavior Driven Development,行为驱动开发。

特点:

  1. 运行系统,模拟用户请求进行访问;
  2. 行为分析要完整,要将可能所有结果覆盖。

示例:

/* 测试路由 */
app.get('/test/model/mysql/init/ok', function (req, res) {'use strict';return db.opensips('v1/subscriber').then(function () {res.send(200, 'ok');}).catch(function (err) {logger('routes/test/model/mysql/ok', err);res.send(403, 'fail');});
});app.get('/test/model/mysql/init/fail', function (req, res) {'use strict';return db.opensips('test/notExisted').then(function () {res.send(200, 'OK');}).catch(function () {res.send(200, 'fail');});
});/* 测试脚本 */
describe('Demo', function () {'use strict';it('404 not found', function (next) {request(app).get('/sth/not/exist').set('Accept', 'text/plain').expect(200).end(function (err, res) {if (err) {throw err;}should(res.body.status).be.equal(0);next();});});it('403 not allowed', function (next) {request(app).get('/v2/basic/mqtt').set('Accept', 'text/plain').expect(200).end(function (err, res) {if (err) {throw err;}should(res.body.status).be.equal(0);next();});});it('Init opensips/subscriber Should be OK', function (next) {request(app).get('/test/model/mysql/init/ok').set('Accept', 'text/plain').expect(200).expect('ok').end(function (err) {if (err) {//console.log(res.body);throw err;}next();});});it('Init test/subscriber Should be FAILED', function (next) {request(app).get('/test/model/mysql/init/fail').set('Accept', 'text/plain').expect(200).expect('fail').end(function (err) {if (err) {//console.log(res.body);throw err;}next();});});
});

ES6 下的 BDD 测试示例对比:

import { test, server, assert } from './_import';
let location;
test.before(async () => {const response = await server.inject({method: 'POST',url: '/login',payload: {username: 'willin',password: 'PASSWORD'}});location = response.headers.location;
});test('GET / 302', async () => {const response = await server.inject({method: 'GET',url: '/'});assert.equal(response.statusCode, 302);
});test('GET /login 200', async () => {const response = await server.inject({method: 'GET',url: '/login'});assert.equal(response.statusCode, 200);
});test('POST /login 302', async () => {const response = await server.inject({method: 'POST',url: '/login',payload: {username: 'willin',password: 'PASSWORD'}});assert.equal(response.statusCode, 302);
});test('POST /login 401', async () => {const response = await server.inject({method: 'POST',url: '/login',payload: {username: 'willin',password: 'Ww10842073305zZa28v3PO5Ok0L63IdA'}});assert.equal(response.statusCode, 401);
});test('POST /login Invalid Params 403', async () => {const response = await server.inject({method: 'POST',url: '/login',payload: {username: 'willin'}});assert.equal(response.statusCode, 403);
});test('GET /doc 200', async () => {const response = await server.inject({method: 'GET',url: location});assert.equal(response.statusCode, 200);
});test('GET /doc 302', async () => {const response = await server.inject({method: 'GET',url: '/doc?'});assert.equal(response.statusCode, 302);
});

忽略细节,就是屎

比如登录表单,输入密码后按回车键一点反应都没有,必须点击登录按钮才能登录。

开发忽略细节,代码一坨屎;设计忽略细节,产品一坨屎。

举个稍微复杂点的例子,消息列表这个功能模块。

假设有两个需求:

  1. 允许用户下拉操作刷新(手动)
  2. 每隔 15s 后台请求刷新(自动)

如果处理不当,会出现漏消息、消息重复等情况。

所有开发环节出现的屎,都能追溯到设计环节。从设计环节就需要考虑好如何规避这些问题的发生。

实践:

0. 消息表说明

在数据库设计章节提到了避免使用自增 id。

所以就不能将消息 id 作为参数传递去查询,而采用时间戳的方式。

1. 参数带入时间戳

时间戳的选取:

  1. 本次请求发起的时间 -> 下次请求可能会重复本次请求处理期间的消息
  2. 本次请求结束的时间 -> 下次请求可能会丢失本次请求处理期间的消息
  3. 最后一条消息记录的时间 -> 完美衔接,最佳选择

2. 阻止请求

如用户狂点导致的频繁刷新,或者用户手动刷新和自动刷新同时进行,都会导致获得到重复消息数据。

先进先出。前一条请求处理完成前,阻止下一条请求。

3. 时间戳的有效性

  1. 如果返回的结果有数据,时间戳应当被更新替换掉
  2. 如果返回的结果没有数据,下次请求依然使用该时间戳

看似简单,其实不易。

优秀工程师(程序员)的定义

很多人对于自己所处的身份并没有十分明确的认识. 技术人最初的身份可能只能算是一个工程师, 或者通俗的叫法为程序员.

价值衡量

如何评判一个工程师是否合格, 并不在于他实现了多少功能, 贡献了多么多的代码.

就像我们在上学的时候也都在提倡德智体全面发展一样. 一个程序员, 本职的工作不仅仅是写代码. 随着科技的发展, 代码的价值会越来越低, 就像搬砖一样, 代码堆彻得多, 无非跟背得砖更多无差, 这种量的增长, 并不能带来更大的价值产出. 是的, 虽然我也一直不愿意面对这样的事实, 但现实是残酷的, 代码是廉价的.

那么该如何评判是否是一名优秀的工程师(程序员)呢? 其实需要通过多个维度去衡量.

1.思想

要让其他人(无论是团队内的领导,同事,下属, 团队外的投资人,客户,用户)都能够轻易理解你的想法.

当然,很多时候我们并不会有很多机会直接与那么多人直接的面对面去沟通交流, 让对方了解你做的这个事情目的是什么, 意义是什么. 这就需要你通过其他的方法来让他们知道.

价值评判物: 设计.

不懂设计, 就写不出好代码.

P.S. 这里的设计, 指的不是用 PS, AI 之类的工具去做平面, UI 的设计. 而是功能, 代码的设计. 在最初的时候, 可以参考借鉴既有的, 成熟的设计模式进行设计.

一份优秀设计的参考标准:

  • 规范的文档和图(模型)

  • 简单, 清晰且全面的流程, 规避无意义的状态扭转

  • Less, More

    • 用更少的描述, 让人更容易理解
    • 用更少的说明, 表达更多的想法

2.效率

这是很多人在拼命追求和改善的, 但在实际的工作中, 我个人的感觉, 效率的确非常重要, 但往往任务不能按时交付并不是某个成员的效率低了.

价值评判物: 单位时间工作产出阶段性工作产出汇总.

很多人会只拿单位时间内的工作产出来衡量效率. 但这样是完全没有道理的, 有的人确实能力很强, 分了任务很快就能完成. 但完成了之后剩下的时间里, 既没有想着这么去优化, 也没有想怎么样自我提升, 就把时间又浪费掉了. 这样的"高效", 真的高效吗? 就像我们从小听的故事, 龟兔赛跑, 兔子睡了一觉, 就被乌龟超了过去.

效率高的参考标准:

  • 同样的一个功能模块开发, 别人需要用 2 个小时完成, 而你只用了 1 个小时, 你的效率高一些. (效率质量往往是需要关联起来衡量的,所以单独拿出来比较并没有任何的参考意义)

  • 时间管理

    • 懂得如何将自己的任务分优先级, 规划得有条理, 并且能按时高质量交付
    • 在团队协作中不浪费他人的时间及资源, 甚至能够帮助其他人提高效率
  • Less, More

    • 更少的时间, 实现更多的功能 (注意, 并非贡献更多的代码)

3.质量

我个人感觉, 质量是最能评判一个优秀工程师的指标了. 因为设计思想, 代码, 算法 等等, 最终实现的成果, 都需要用质量来评判.

价值评判物: ? (真的是很难说什么东西能够直接体现出工作产出质量的)

参考评判物: (按权重降序排列)

  1. 文档(设计)
  2. 测试报告
  3. 性能分析报告
  4. 代码(主要衡量: 业务逻辑, 算法)

质量评判参考标准:

  • 各类文档完善程度

  • TDD/BDD 测试覆盖率 (95%以上)

  • 性能分析报告 (Apdex 性能 0.9 分以上, Bug 率 1%以下)

  • 代码注释率 (10% 以上)

  • 代码重复率 (10% 以下)

  • Less, More

    • 更少的代码, 实现更多的功能
    • 更少的代码块, 更高的执行效率
    • 更少的测试用例, 覆盖更多的可能性
    • 更少的成本浪费, 更多的价值产出

DevOps 全栈开发基础相关推荐

  1. Web全栈开发基础(小白入门版本)

    博客传送门 近几个月认真写了写Web全栈代码,有点小收获这里分享一下.我还做了个PPT,资源路径 欢迎拍砖指点! Web全栈开发是一个听起来很虎的名词.本文从技术层面解释全栈开发,能帮助没有全栈概念, ...

  2. python全栈开发基础【补充】包的补充

    1.包A和包B下有同名模块也不会冲突,因为A.a与B.a来自俩个命名空间 2.常见目录结构 # 创建目录代码 import os os.makedirs('glance/api') os.makedi ...

  3. python全栈开发百度云_老男孩2020最新Python全栈开发基础班+就业班

    |- 数据结构+算法.rar - 485.30 MB |- 串讲.rar - 2.01 GB |- 补充资料.rar - 536.00 MB |- MongoDB.rar - 110.10 MB |- ...

  4. python全栈开发基础【第十七篇】面向对象反射和内置方法

    一.静态方法(staticmethod)和类方法(classmethod) 类方法:有个默认参数cls,并且可以直接用类名去调用,可以与类属性交互(也就是可以使用类属性) 静态方法:让类里的方法直接被 ...

  5. python全栈开发基础【第二十三篇】线程

    一.什么是线程 线程:顾名思义,就是一条流水线工作的过程,一条流水线必须属于一个车间,一个车间的工作过程是一个进程 所以,进程只是用来把资源集中到一起(进程只是一个资源单位,或者说资源集合),而线程才 ...

  6. Python 全栈开发基础

    python面向对象 python异常处理 python网络编程 python并发编程 临时目录 转载于:https://www.cnblogs.com/fixdq/p/8883304.html

  7. python进程通信方式有几种_python全栈开发基础【第二十一篇】互斥锁以及进程之间的三种通信方式(IPC)以及生产者个消费者模型...

    一.互斥锁 进程之间数据隔离,但是共享一套文件系统,因而可以通过文件来实现进程直接的通信,但问题是必须自己加锁处理. 注意:加锁的目的是为了保证多个进程修改同一块数据时,同一时间只能有一个修改,即串行 ...

  8. 使用 MEAN 进行全栈开发基础篇——2、弄一个简单的用户管理试试

    搭建项目 这下我们应该来搭建一个项目了,随便起个名字 ForUsers 找一个合适的位置,然后打开命令行:express ForUsers 会得如下树状图的结构 ├── app.js ├── bin ...

  9. 使用 MEAN 进行全栈开发基础篇——4、接着前面玩儿添加

    添加一个新用户 在本节中,你将了解有关使用 Express 创建 API 端点,使用 Angular 构建表单,以及使用 Monk 在 Mongo 中存储内容的更多信息. 与上一节类似,但在本节中我们 ...

最新文章

  1. webview加载本地资源的各种尝试
  2. 基于阿里云搭建的适合初创企业的轻量级架构--架构总结
  3. 重新想象 Windows 8 Store Apps (49) - 输入: 获取输入设备信息, 虚拟键盘, Tab 导航, Pointer, Tap, Drag, Drop...
  4. c++ 结构体初始化_【干货】c语言基础语法——结构体
  5. 如何通过css控制内容显示顺序 第二行的内容优先显示
  6. 火星云分发全网视频_火星云矿总裁商思林:三大引擎点燃2020年超级牛市,挖矿和理财将成为资产配置必选项...
  7. 如何对抗硬件断点--- 调试寄存器
  8. 阿里云产品头条(2017年12月刊)
  9. 百度地图个性化模板列表
  10. 2018年蓝桥杯C++B;乘积尾零(拆分法)
  11. 算法——K均值聚类算法(Java实现)
  12. mybait-plus实现动态自定义查询条件
  13. 计算机Word2010中刷新键,Office2010常用快捷键汇总(最新整理)
  14. 一分钟搞懂NB-IoT行业发展
  15. Compile chromium OS on Debian
  16. ERP是属于系统还是软件?
  17. 江城子/乙卯正月二十日夜记梦——苏轼
  18. java微信提现_如何做提现到微信和支付宝
  19. 拨开历史的迷雾从篡夺者战争到五王之战的政经原因
  20. 坚持十年博客写作,不忘初心,方得始终

热门文章

  1. MQTT入门2 -- “Error: Invalid password hash for user nick.”和“Connection Refused: not authorised.”...
  2. 速卖通代运营说:速卖通太难了!2020去做不算晚,但是也绝对不早!
  3. 山东省高中学业水平考试计算机考试教材,2017年山东高中学业水平考试科目
  4. html5中歌曲和歌词同步的方法
  5. UIAutomatorViewer初体验
  6. Spring_背诵英文单词
  7. 11. JS编程之查找元素在数组中的位置
  8. C# 将dataTable中的数据导出到excel表中
  9. 2019.4.8-pandas 读写csv;dorp删除行、列
  10. 软件工程(3)微信抢票应用个人总结