GitHub 漫游指南

项目首页: GitHub 漫游指南

By Phodal Huang(微博、知乎、GitHub、SegmentFault: @phodal)

我的其他电子书:

微信公众号

当前为预览版,在使用的过程中遇到任何遇到请及时与我联系。阅读过程中问题,不烦在GitHub上提出来: Issues

阅读过程中遇到语法错误、拼写错误、技术错误等等,不烦来个Pull Request,这样可以帮助到其他阅读这本电子书的童鞋。

前言

关于作者

黄峰达(Phodal Huang)是一个创客、工程师、咨询师和作家。他毕业于西安文理学院电子信息工程专业,现作为一个咨询师就职于 ThoughtWorks 深圳。长期活跃于开源软件社区 GitHub,目前专注于物联网和前端领域。

作为一个开源软件作者,著有 Growth、Stepping、Lan、Echoesworks 等软件。其中开源学习应用 Growth,广受读者和用户好评,可在 APP Store 及各大 Android 应用商店下载。

作为一个技术作者,著有《自己动手设计物联网》(电子工业出版社)、《全栈应用开发:精益实践》(电子工业出版社,正在出版)。并在 GitHub 上开源有《Growth: 全栈增长工程师指南》、《GitHub 漫游指南》等七本电子书。

作为技术专家,他为英国 Packt 出版社审阅有物联网书籍《Learning IoT》、《Smart IoT》,前端书籍《Angular 2 Serices》、《Getting started with Angular》等技术书籍。

他热爱编程、写作、设计、旅行、hacking,你可以从他的个人网站:https://www.phodal.com/ 了解到更多的内容。

其它相关信息:

当前为预览版,在使用的过程中遇到任何问题请及时与我联系。阅读过程中的问题,不妨在GitHub上提出来: Issues

阅读过程中遇到语法错误、拼写错误、技术错误等等,不妨来个Pull Request,这样可以帮助到其他阅读这本电子书的童鞋。

我的电子书:

我的微信公众号:

作者微信公众号:phodal-weixin
作者微信公众号:phodal-weixin

支持作者,可以加入作者的小密圈:

小密圈
小密圈

或者转账:

支付宝 微信

我的GitHub主页上写着加入的时间——Joined on Nov 8, 2010,那时才大一,在那之后的那么长的日子里我都没有登录过。也许是因为我学的不是计算机,到了今天——2015.3.9,我才发现这其实是程序员的社交网站。

过去,曾经有很长的一些时间我试过在GitHub上连击,也试着去了解别人是如何用好这个工具的。当然粉丝在GitHub上也是很重要的。

在这里,我会试着将我在GitHub上学到的东西一一分享出来。

我与GitHub的故事

在我大四找工作的时候,试图去寻找一份硬件、物联网相关的工作(ps: 专业是电子信息工程)。尽管简历上写得满满的各种经历、经验,然而并没有卵用。跑了几场校园招聘会后,十份简历(ps: 事先已经有心里准备)一个也没有投出去——因为学校直接被拒。我对霸面什么的一点兴趣都没有,千里马需要伯乐。后来,我加入了Martin Flower所在的公司,当然这是后话了。

这是一个残酷的世界,在学生时代,如果你长得不帅不高的话,那么多数的附加技能都是白搭(ps: 通常富的是看不到这篇文章的)。在工作时期,如果你上家没有名气,那么将会影响你下一份工作的待遇。而,很多东西却可以改变这些,GitHub就是其中一个。

注册GitHub的时候大概是大一的时候,我熟悉的时候已经是大四了,现在已经毕业一年了。在过去的近两年里,我试着以几个维度在GitHub上创建项目:

  1. 快速上手框架来实战,即demo
  2. 重构别人的代码
  3. 创建自己可用的框架
  4. 快速构建大型应用
  5. 构建通用的框架

GitHub与收获

先说说与技能无关的收获吧,毕业设计做的是一个《最小物联网系统》,考虑到我们专业老师没有这方面知识,答辩时会带来问题,尽量往这方面靠拢。当我毕业后,这个项目已经有过百个star了,这样易上手的东西还是比较受欢迎的(ps: 不过这种硬件相关的项目通常受限于GitHub上硬件开发工程师比较少的困扰)。

毕业后一个月收到PACKT出版社的邮件(ps: 他们是在github上找到我的),内容是关于Review一本物联网书籍,即在《从Review到翻译IT书籍》中提到的《Learning Internet of Things》。作为一个四级没过的“物联网专家”,去审阅一本英文的物联网书籍。。。

当然,后来是审阅完了,书上有我的英文简介。

Phodal Huang Introduction
Phodal Huang Introduction

一个月前,收到MANNING出版社的邮件(ps: 也是在github上),关于Review一本物联网书籍的目录,并提出建议。

也因此带来了其他更多的东西,当然不是这里的主题。在这里,我们就不讨论各种骚扰邮件,或者中文合作。从没有想象过,我也可以在英语世界有一片小天地。

这些告诉我们,GitHub上找一个你擅长的主题,那么会有很多人找上你的。

GitHub与成长

过去写过一篇《如何通过github提升自己》的文章,现在只想说三点:

  1. 测试
  2. 更多的测试
  3. 更多的、更多的、更多的测试

没有测试的项目是很扯淡的,除非你的项目只有一个函数,然后那个函数返回Hello,World

如果你的项目代码有上千行,如果你能保证测试覆盖率可以达到95%以的话,那么我想你的项目不会有太复杂的函数。假使有这样的函数,那么它也是被测试覆盖住的。

如果你在用心做这个项目,那么你看到代码写得不好也会试着改进,即重构。当有了一些,你的技能会不断提升。你开始会试着接触更多的东西,如stub,如mock,如fakeserver。

有一天,你会发现你离不开测试。

然后就会相信: 那些没有写测试的项目都是在耍流氓

为什么你应该深入GitHub

上面我们说的都是我们可以收获到的东西,我们开始尝试就意味着我们知道它可能给我们带来好处。上面已经提到很多可以提升自己的例子了,这里再说说其他的。

方便工作

我们可以从中获取到不同的知识、内容、信息。每个人都可以从别人的代码中学习,当我们需要构建一个库的时候,我们可以在上面寻找不同的库和代码来实现我们的功能。如当我在实现一个库的时候,我会在GitHub上找到相应的组件:

获得一份工作

越来越多的人因为GitHub获得工作,因为他们的做的东西正好符合一些公司的要求。那么,这些公司在寻找代码的时候,就会试着邀请他们。

因而,在GitHub寻找合适的候选人,已经是一种趋势。

扩大交际

如果我们想创造出更好、强大地框架时,那么认识更多的人可能会带来更多的帮助。有时候会同上面那一点一样的效果

创建开源项目

人们出于不同的目的来创建开源项目,可不论目的是什么,过程都是一样的。

  1. 首先,我们需要为我们的项目取一个名字。
  2. 然后,为我们的开源项目选择一个合适的 LICENSE
  3. 然后再去创建项目

取一个好的名字

取名字,从来就不是一件容易的事。

因此,我就长话短说,一般就是取一个有意义的名字,当然没有意义也没有任何问题。

通常而言,如果自己计划有一系列的开源项目,那么我们可以保持一定的命令规则。

挑选好 LICENSE

在二十世纪而七十年代末和八十年代初,为了防止自己的软件被竞争对手所使用,大多数厂家停止分发其软件源代码,并开始使用版权和限制性软件许可证,来限制或者禁止软件源代码的复制或再分配。随后,Richard Matthew Stallman(Richard Matthew Stallman)发起了自由软件运动,他开创了 Copyleft 的概念:使用版权法的原则来保护使用、修改和分发自由软件的权利,并且是描述这些术语的自由软件许可证的主要作者。最为人所称道的是GPL(被z广泛使用的自由软件协议)。1

(PS:关于自由软件及 RMS 的更多信息、历史,可以阅读《若为自由故:自由软件之父 - 理查德 斯托曼传》)

随后,便诞生了开源软件的概念,开源的要求比自由软件宽松一些2。迄今发布的自由软件源代码都是开源软件,而并非所有的开源软件都是自由软件。这是因为不同的许可(协议)赋予用户不同的权利,如 GPL 协议强制要求开源修改过源码的代码,而宽松一点的 MIT 则不会有这种要求。

如下是不同开源许可证的市场占有率及使用情况。

License 使用情况
License 使用情况

又比如,在我们看到的一些外版书籍上,如果拥有代码。那么作者一般就会在前言或者类似的位置里,指明书中代码的版权所属。如:

也许你需要在自己的程序或文档中用到本书的代码,但除非大篇幅地使用,否则不必与我们联系取得授权。例如,用本书中的几段代码编写程序无需请求许可,blabla。

于是,选择一个合理的 LICENSE,就变成了一个有趣的话题。为此,笔者做了一个如何进行开源协议选型的流程图:

如何选择 License

简单地来说,这些 License 之间是一些权利的区别,如当你把代码放置到公有领域,就意味着任何人可以修改,并且不需要标明出注;可如果你想要别人标明出处及作者,你就需要 MIT 协议;而你希望别人闭源的话,那么你就需要 MPL 协议等等。

那么,下面让我们简单地介绍一下不同的几个协议。

公有领域

WTFPL(Do What The Fuck You Want To Public License,中文译名:你他妈的想干嘛就干嘛公共许可证)是一种不太常用的、极度放任的自由软件许可证。它的条款基本等同于贡献到公有领域。3

这就意味着,对于拿到这些代码的其他人,他们想怎么修改就可以怎么修改。

这取决于

GPL

由于 GPL 的传染性,便意味着,他人引用我们的代码时,其所写的代码也需要使用 GPL 开源。即:GPL 是有 “传染性” 的 “病毒” ,因为 GPL 条款规定演绎作品也必须是 GPL 的。

而如果我们只针对的是,他人可以使用库,而不开源,则可以用 LGPL。但是修改库则不适用。

MIT

因此,一般而言,我使用的是 MIT 协议。至少我保留了一个署名权,即你可以修改我的代码,但是在 LICENSE 里必须加上我的名字。

选用 MIT 特别有意思,特别是在最近几年里,发生过:

等等。这告诫了我们,如果你不想要有这种经历,那么就不要用 MIT 了。

Creative Commons

是的,当我写 Markdown 的时候,考虑到未来会以纸质书的形式出现,便会使用 CC-BY-NC-ND 协议:

即,任何人可以使用我写的电子书来自由复制、散布、展示及演出,但是不得用于商业用途(作者本人可以)。它可以随意地放在他的博客上,他的各个文章里。但是必须标明出自,并且不得改变、转变或更改本作品。

如果你不介意的话,你可以使用公有领域(Public Domain)。可是这样一来,万一有一天,别人直接拿的作品出书,你就骂爹了。

Git基本知识与GitHub使用

Git

从一般开发者的角度来看,git有以下功能:

  1. 从服务器上克隆数据库(包括代码和版本信息)到单机上。
  2. 在自己的机器上创建分支,修改代码。
  3. 在单机上自己创建的分支上提交代码。
  4. 在单机上合并分支。
  5. 新建一个分支,把服务器上最新版的代码fetch下来,然后跟自己的主分支合并。
  6. 生成补丁(patch),把补丁发送给主开发者。
  7. 看主开发者的反馈,如果主开发者发现两个一般开发者之间有冲突(他们之间可以合作解决的冲突),就会要求他们先解决冲突,然后再由其中一个人提交。如果主开发者可以自己解决,或者没有冲突,就通过。
  8. 一般开发者之间解决冲突的方法,开发者之间可以使用pull 命令解决冲突,解决完冲突之后再向主开发者提交补丁。

从主开发者的角度(假设主开发者不用开发代码)看,git有以下功能:

  1. 查看邮件或者通过其它方式查看一般开发者的提交状态。
  2. 打上补丁,解决冲突(可以自己解决,也可以要求开发者之间解决以后再重新提交,如果是开源项目,还要决定哪些补丁有用,哪些不用)。
  3. 向公共服务器提交结果,然后通知所有开发人员。

Git初入

如果是第一次使用Git,你需要设置署名和邮箱:

$ git config --global user.name "用户名"
$ git config --global user.email "电子邮箱"

将代码仓库clone到本地,其实就是将代码复制到你的机器里,并交由Git来管理:

$ git clone git@github.com:someone/symfony-docs-chs.git

你可以修改复制到本地的代码了(symfony-docs-chs项目里都是rst格式的文档)。当你觉得完成了一定的工作量,想做个阶段性的提交:

向这个本地的代码仓库添加当前目录的所有改动:

$ git add .

或者只是添加某个文件:

$ git add -p

我们可以输入

$git status

来看现在的状态,如下图是添加之前的:

Before add
Before add

下面是添加之后 的

After add
After add

可以看到状态的变化是从黄色到绿色,即unstage到add。

GitHub

Wiki百科上是这么说的

GitHub 是一个共享虚拟主机服务,用于存放使用Git版本控制的软件代码和内容项目。它由GitHub公司(曾称Logical Awesome)的开发者Chris Wanstrath、PJ Hyett和Tom Preston-Werner 使用Ruby on Rails编写而成。

当然让我们看看官方的介绍:

GitHub is the best place to share code with friends, co-workers, classmates, and complete strangers. Over eight million people use GitHub to build amazing things together.

它还是什么?

等等。看上去像是大餐,但是你还需要了解点什么?

版本管理与软件部署

jQuery[^jQuery]在发布版本2.1.3,一共有152个commit。我们可以看到如下的提交信息:

GitHub与Git

Git是一个分布式的版本控制系统,最初由Linus Torvalds编写,用作Linux内核代码的管理。在推出后,Git在其它项目中也取得了很大成功,尤其是在Ruby社区中。目前,包括Rubinius、Merb和Bitcoin在内的很多知名项目都使用了Git。Git同样可以被诸如Capistrano和Vlad the Deployer这样的部署工具所使用。

GitHub可以托管各种git库,并提供一个web界面,但与其它像 SourceForge或Google Code这样的服务不同,GitHub的独特卖点在于从另外一个项目进行分支的简易性。为一个项目贡献代码非常简单:首先点击项目站点的“fork”的按钮,然后将代码检出并将修改加入到刚才分出的代码库中,最后通过内建的“pull request”机制向项目负责人申请代码合并。已经有人将GitHub称为代码玩家的MySpace。

在 GitHub 创建项目

接着,我们试试在上面创建一个项目:

GitHub Roam
GitHub Roam

就会有下面的提醒:

GitHub Roam
GitHub Roam

它提供多种方式的创建方法:

…or create a new repository on the command line

echo "# github-roam" >> README.md
git init
git add README.md
git commit -m "first commit"
git remote add origin git@github.com:phodal/github-roam.git
git push -u origin master

…or push an existing repository from the command line

git remote add origin git@github.com:phodal/github-roam.git
git push -u origin master

如果你完成了上面的步骤之后,那么我想你想知道你需要怎样的项目。

GitHub 流行项目分析

之前曾经分析过一些GitHub的用户行为,现在我们先来说说GitHub上的Star吧。(截止: 2015年3月9日23时。)

用户 项目名 Language Star Url
twbs Bootstrap CSS 78490 https://github.com/twbs/bootstrap
vhf free-programming books - 37240 https://github.com/vhf/free-programming-books
angular angular.js JavaScript 36,061 https://github.com/angular/angular.js
mbostock d3 JavaScript 35,257 https://github.com/mbostock/d3
joyent node JavaScript 35,077 https://github.com/joyent/node

上面列出来的是前5的,看看大于1万个stars的项目的分布,一共有82个:

语言 项目数
JavaScript 37
Ruby 6
CSS 6
Python 4
HTML 3
C++ 3
VimL 2
Shell 2
Go 2
C 2

类型分布:

Pull Request

除了创建项目之外,我们也可以创建Pull Request来做贡献。

我的第一个PR

我的第一个PR是给一个小的Node的CoAP相关的库的Pull Request。原因比较简单,是因为它的README.md写错了,导致我无法进行下一步。

     const dgram       = require('dgram')
    -    , coapPacket  = require('coap-packet')
    +    , package     = require('coap-packet')

很简单,却又很有用的步骤,另外一个也是:

 else
   cat << END
 $0: error: module ngx_pagespeed requires the pagespeed optimization library.
-Look in obj/autoconf.err for more details.
+Look in objs/autoconf.err for more details.
 END
   exit 1
 fi

CLA

CLA即Contributor License Agreement,在为一些大的组织、机构提交Pull Request的时候,可能需要签署这个协议。他们会在你的Pull Request里问你,只有你到他们的网站去注册并同意协议才会接受你的PR。

以下是我为Google提交的一个PR

Google CLA
Google CLA

以及Eclipse的一个PR

Eclipse CLA
Eclipse CLA

他们都要求我签署CLA。

构建 GitHub 项目

如何用好 GitHub

如何用好 GitHub,并实践一些敏捷软件开发是一个很有意思的事情.我们可以在上面做很多事情,从测试到CI,再到自动部署.

敏捷软件开发

显然我是在扯淡,这和敏捷软件开发没有什么关系。不过我也不知道瀑布流是怎样的。说说我所知道的一个项目的组成吧:

对于一个不是远程的团队(如只有一个人的项目) 来说,Trello、Jenkin、Jira不是必需的:

你存在,我深深的脑海里

当只有一个人的时候,你只需要明确知道自己想要什么就够了。我们还需要的是CI、测试,以来提升代码的质量。

测试

通常我们都会找Document,如果没有的话,你会找什么?看源代码,还是看测试?

代码来源: https://github.com/phodal/lettuce

上面的测试用例,清清楚楚地写明了用法,虽然写得有点扯。

等等,测试是用来干什么的。那么,先说说我为什么会想去写测试吧:

虽然,我不是TDD的死忠,测试的目的是保证功能正常,TDD没法让我们写出质量更高的代码。但是有时TDD是不错的,可以让我们写出逻辑更简单地代码。

也许你已经知道了SeleniumJasmineCucumber等等的框架,看到过类似于下面的测试

 Ajax
   ✓ specifying response when you need it
   ✓ specifying html when you need it
   ✓ should be post to some where
 Class
   ✓ respects instanceof
   ✓ inherits methods (also super)
   ✓ extend methods
 Effect
   ✓ should be able fadein elements
   ✓ should be able fadeout elements

代码来源: https://github.com/phodal/lettuce

看上去似乎每个测试都很小,不过补完每一个测试之后我们就得到了测试覆盖率

File Statements Branches Functions Lines
lettuce.js 98.58% (209 / 212) 82.98%(78 / 94) 100.00% (54 / 54) 98.58% (209 / 212)

本地测试都通过了,于是我们添加了Travis-CI来跑我们的测试

CI

虽然node.js不算是一门语言,但是因为我们用的node,下面的是一个简单的.travis.yml示例:

代码来源: https://github.com/phodal/lettuce

我们把这些集成到README.md之后,就有了之前那张图。

CI对于一个开发者在不同城市开发同一项目上来说是很重要的,这意味着当你添加的部分功能有测试覆盖的时候,项目代码会更加强壮。

代码质量

jslint这类的工具,只能保证代码在语法上是正确的,但是不能保证你写了一堆bad smell的代码。

Code Climate是一个与github集成的工具,我们不仅仅可以看到测试覆盖率,还有代码质量。

先看看上面的ajax类:

代码来源: https://github.com/phodal/lettuce

Code Climate在出现了一堆问题

而这些都是小问题啦,有时可能会有

这就意味着我们可以对上面的代码进行重构,他们是重复的代码。

模块分离与测试

在之前说到

奋斗了近半个月后,将fork的代码读懂、重构、升级版本、调整,添加新功能、添加测试、添加CI、添加分享之后,终于almost finish。

今天就来说说是怎样做的。

以之前造的Lettuce为例,里面有:

按照Web Developer路线图来说,我们还需要有:

等等。

代码模块化

在SkillTree的源码里,大致分为三部分:

而这一些都在一个js里,对于一个库来说,是一件好事,但是对于一个项目来说,并非如此。

依赖的库有

好在Knockout可以用Require.js进行管理,于是,使用了Require.js进行管理:

main.js配置如下:

text、json插件主要是用于处理web.json,即用json来处理技能,于是不同的类到了不同的js文件。

.
|____Book.js
|____Doc.js
|____ko-bindings.js
|____Link.js
|____main.js
|____Skill.js
|____TalentTree.js
|____Utils.js

加上了后来的推荐阅读书籍等等。而Book和Link都是继承自Doc。

而这里便是后面对其进行重构的内容。Doc类则是Skillock中类的一个缩影

或者说这是一个AMD的Class应该有的样子。考虑到this的隐性绑定,作者用了self=this来避免这个问题。最后Return了这个对象,我们在调用的就需要new一个。大部分在代码中返回的都是对象,除了在Utils类里面返回的是函数:

当然函数也是一个对象。

自动化测试

一直习惯用Travis CI,于是也继续用Travis Ci,.travis.yml配置如下所示:

使用gh-pages的原因是,我们一push代码的时候,就可以自动测试、部署等等,好处一堆堆的。

接着我们需要在package.json里面添加脚本

这样当我们push代码的时候便会自动跑所有的测试。因为mocha的主要配置是用mocha.opts,所以我们还需要配置一下mocha.opts

--reporter spec
--ui bdd
--growl
--colors
test/spec     

最后的test/spec是指定测试的目录。

Jshint

JSLint定义了一组编码约定,这比ECMA定义的语言更为严格。这些编码约定汲取了多年来的丰富编码经验,并以一条年代久远的编程原则 作为宗旨:能做并不意味着应该做。JSLint会对它认为有的编码实践加标志,另外还会指出哪些是明显的错误,从而促使你养成好的 JavaScript编码习惯。

当我们的js写得不合理的时候,这时测试就无法通过:

line 5   col 25   A constructor name should start with an uppercase letter.
line 21  col 62   Strings must use singlequote.

这是一种驱动写出更规范js的方法。

Mocha

Mocha 是一个优秀的JS测试框架,支持TDD/BDD,结合 should.js/expect/chai/better-assert,能轻松构建各种风格的测试用例。

最后的效果如下所示:

Book,Link
  Book Test
    ✓ should return book label & url
  Link Test
    ✓ should return link label & url

测试示例

简单地看一下Book的测试:

因为我们用require.js来管理浏览器端,在后台写测试来测试的时候,我们也需要用他来管理我们的依赖,这也就是为什么这个测试这么长的原因,多数情况下一个测试类似于这样子的。(用Jasmine似乎会是一个更好的主意,但是用习惯Jasmine了)

最后的断言,也算是测试的核心,保证测试是有用的。

代码质量与重构

这就是个问题了,于是偶然间看到了一个叫code climate的网站。

Code Climate

Code Climate consolidates the results from a suite of static analysis tools into a single, real-time report, giving your team the information it needs to identify hotspots, evaluate new approaches, and improve code quality.

Code Climate整合一组静态分析工具的结果到一个单一的,实时的报告,让您的团队需要识别热点,探讨新的方法,提高代码质量的信息。

简单地来说:

于是,我们先来了个例子

Rating Name Complexity Duplication Churn C/M Coverage
A lib/coap/coap_request_handler.js 24 0 6 2.6 46.4%
A lib/coap/coap_result_helper.js 14 0 2 3.4 80.0%
A lib/coap/coap_server.js 16 0 5 5.2 44.0%
A lib/database/db_factory.js 8 0 3 3.8 92.3%
A lib/database/iot_db.js 7 0 6 1.0 58.8%
A lib/database/mongodb_helper.js 63 0 11 4.5 35.0%
C lib/database/sqlite_helper.js 32 86 10 4.5 35.0%
B lib/rest/rest_helper.js 19 62 3 4.7 37.5%
A lib/rest/rest_server.js 17 0 2 8.6 88.9%
A lib/url_handler.js 9 0 5 2.2 94.1%

分享得到的最后的结果是:

[Coverage][1]

代码的坏味道

于是我们就打开lib/database/sqlite_helper.js,因为其中有两个坏味道

Similar code found in two :expression_statement nodes (mass = 86)

在代码的 lib/database/sqlite_helper.js:58…61 < >

lib/database/sqlite_helper.js:64…67 < >

只是这是之前修改过的重复。。

原来的代码是这样的

说的也是大量的重复,重构完的代码

重构完后的代码比原来还长,这似乎是个问题~~

Git 提交信息及几种不同的规范

受 Growth 3.0 开发的影响,最近更新文章的频率会有所降低。今天,让我们来谈谈一个好的 Git、SVN 提交信息是怎样规范出来的。

在团队协作中,使用版本管理工具 Git、SVN 几乎都是这个行业的标准。当我们提交代码的时候,需要编写提交信息(commit message)。

而提交信息的主要用途是:告诉这个项目的人,这次代码提交里做了些什么。如,我更新了 React Native Elements 的版本,那么它就可以是:[T] upgrade react native elements。对应的我修改的代码就是:package.jsonyarn.lock 中的文件。一般来说,建议小步提交,即按自己的 Tasking 步骤来的提交,每一小步都有对应的提交信息。这样做的主要目的是:防止一次修改中,修改过多的文件,导致后期修改、维护、撤销等等困难

而对于不同的团队来说,都会遵循一定的规范,本文主要会介绍以下几种写法:

那么,先从我习惯的做法说起。

工作写法

在我的第一个项目里,我们使用 Jira 作为看板工具,Bamboo 作为持续集成服务器,并采用结对编程的方式进行。

在 Jira 里每一个功能卡都有对应的卡号,而 Bamboo 支持使用 Jira 的任务卡号关联的功能。即在持续构建服务器上示例对应的任务卡号,即相应的提交人。

因此,这个时候我们的规范稍微有一些特别:

[任务卡号] xx & xx: do something 

比如:[PHODAL-0001] ladohp & phodal: update documents,解释如下:

缺点:而对于采用看板的团队来说,并不存在任务卡号这种东西,因此就需要一种额外的作法。

常规写法

对于我来说,我则习惯这种的写法:

[任务分类] 主要修改组件(可选):修改内容

示例 1,[T] tabs: add icons 。其中的 T 表示这是一个技术卡,tabs 表示修改的是 Tabs,add icons 则表示添加了图标。

示例 2,[SkillTree] detail: add link data。其中的 SkillTree 表示修改的是技能树 Tab 下的内容,detail 则表示修改的是详情页,add link data 则表示是添加了技能的数据

这样做的主要原因是,它可以轻松也帮我** filter 出相应业务的内容**。

缺点:要这样做需要团队达到一致,因此付出一些额外的成本。

开源应用、开源库写法

与我们日常工作稍有不同的是:工作中的 Release 计划一般都是事先安排好的,不需要一些 CHANGELOG 什么的。而开源应用、开源库需要有对应的 CHANELOG,则添加了什么功能、修改了什么等等。毕竟有很多东西是由社区来维护的。

因此,这里以做得比较好的开源项目 Angular 为例展示。Angular 团队建议采用以下的形式:

<type>(<scope>): <subject>
<BLANK LINE>
<body>
<BLANK LINE>
<footer>

诸如:docs(changelog): update change log to beta.5 中:

对应的类型有:

同时还对应了 20+ 的 Scope,可以说这种提交比上面的提交更有挑战。

(以上的 10 个类型,感谢 Google Translate 提供的快速翻译支持)

而这样做的优点是,它可以轻松地生成一个 CHANGELOG。与些同时还有一个名为 Conventional Commits 的规范,建议采用相似的形式。

创建项目文档

我们需要为我们的项目创建一个文档,通常我们可以将核心代码以外的东西都称为文档:

  1. README
  2. 文档
  3. 示例
  4. 测试

通常这个会在项目的最上方会有一个项目的简介,如下图所示:

GitHub Project Introduction
GitHub Project Introduction

README

README通常会显示在GitHub项目的下面,如下图所示:

GitHub README
GitHub README

通常一个好的README会让你立马对项目产生兴趣。

如下面的内容是React项目的简介:

React README
React README

下面的内容写清楚了他们的用途:

通常在这个README里,还会有:

官方首页与在线文档

很多开源项目都会有自己的网站,并在上面有一个文档,而有的则会放在https://readthedocs.org/

Read the Docs 托管文档,让文档可以被全文搜索和更易查找。您可以导入您使用任何常用的版本控制系统管理的文档,包括 Mercurial、Git、Subversion 和 Bazaar。 我们支持 webhooks,因此可以在您提交代码时自动构建文档。并且同样也支持版本功能,因此您可以构建来自您代码仓库中某个标签或分支的文档。查看完整的功能列表 。

在一个开源项目中,良好和专业的文档是相当重要的,有时他可能会比软件还会重要。因为如果一个开源项目好用的话,多数人可能不会去查看软件的代码。这就意味着,多数时候他在和你的文档打交道。文档一般会有:API 文档、 配置文档、帮助文档、用户手册、教程等等

写文档的软件有很多,如Markdown、Doxygen、Docbook等等。

可用示例

一个简单上手的示例非常重要,特别是通常我们是在为着某个目的而去使用一个开源项目的是时候,我们希望能马上使用到我们的项目中。

你希望看到的是,你打开浏览器,输入下面的代码,然后It Works:

var HelloMessage = React.createClass({
  render: function() {
    return <div>Hello {this.props.name}</div>;
  }
});

React.render(
  <HelloMessage name="John" />,
  document.getElementById('container')
);

而不是需要繁琐的步骤才能进行下一步。

改善 GitHub 项目代码质量:重构

或许你应该知道了,重构是怎样的,你也知道重构能带来什么。在我刚开始学重构和设计模式的时候,我需要去找一些好的示例,以便于我更好的学习。有时候不得不创造一些更好的场景,来实现这些功能。

有一天,我发现当我需要我一次又一次地重复讲述某些内容,于是我就计划着把这些应该掌握的技能放到GitHub上,也就有了Artisan Stack 计划。

每个程序员都不可避免地是一个Coder,一个没有掌握好技能的Coder,算不上是手工艺人,但是手工艺人,需要有创造性的方法。

为什么重构?

为了更好的代码。

在经历了一年多的工作之后,我平时的主要工作就是修Bug。刚开始的时候觉得无聊,后来才发现修Bug需要更好的技术。有时候你可能要面对着一坨一坨的代码,有时候你可能要花几天的时间去阅读代码。而你重写那几十行代码可能只会花上你不到一天的时间。但是如果你没办法理解当时为什么这么做,你的修改只会带来更多的Bug。修Bug,更多的是维护代码。还是前人总结的那句话对:

写代码容易,读代码难。

假设我们写这些代码只要半天,而别人读起来要一天。为什么不试着用一天的时候去写这些代码,让别人花半天或者更少的时间来理解。

如果你的代码已经上线,虽然是一坨坨的。但是不要轻易尝试没有测试的重构

从前端开始的原因在于,写得一坨坨且最不容易测试的代码都在前端。

让我们来看看我们的第一个训练,相当有挑战性。

重构uMarkdown

代码及setup请见github: js-refactor

代码说明

uMarkdown是一个用于将Markdown转化为HTML的库。代码看上去就像一个很典型的过程代码:

选这个做重构的开始,不仅仅是因为之前在写EchoesWorks的时候进行了很多的重构。而且它更适合于重构到设计模式的理论。让我们在重构完之后,给作者进行pull request吧。

Markdown的解析过程,有点类似于Pipe and Filters模式(架构模式)。

Filter即我们在代码中看到的正规表达式集:

他会匹配对应的Markdown类型,随后进行替换和处理。而``str```,就是管理口的输入和输出。

接着,我们就可以对其进行简单的重构。

(ps: 推荐用WebStrom来做重构,自带重构功能)

作为一个示例,我们先提出codeHandler方法,即将上面的

提取方法成

while语句就成了

然后,运行所有的测试。

grunt test

同理我们就可以mailheadline等方法进行重构。接着就会变成类似于下面的代码,

然后你也看到了,上面有一堆重复的代码,接着让我们用JavaScript的奇技淫巧,即apply方法,把上面的重复代码变成。

进行测试,blabla,都是过的。

快来试试吧, https://github.com/artisanstack/js-refactor

是时候讨论这个Refactor利器了,最初看到这个重构的过程是从ThoughtWorks郑大晔校开始的,只是之前对于Java的另外一个编辑器Eclipse的坏感。。这些在目前已经不是很重要了,试试这个公司里面应用广泛的编辑器。

Intellij Idea重构

开发的流程大致就是这样子的,测试先行算是推荐的。

编写测试->功能代码->修改测试->重构

上次在和buddy聊天的时候,才知道测试在功能简单的时候是后行的,在功能复杂不知道怎么下手的时候是先行的。

开始之前请原谅我对于Java语言的一些无知,然后,看一下我写的Main函数:

代码写得还好(自我感觉),先不管Cal和Cal2两个类。大部分都能看懂,除了c,d不知道他们表达的是什么意思,于是。

Rename

快捷键:Shift+F6

作用:重命名

于是就有

Extract Method

快捷键:alt+command+m

作用:扩展方法

于是有了

似乎我们不应该这样对待System.out.println,那么让我们内联回去

Inline Method

快捷键:alt+command+n

作用:内联方法

然后我们等于什么也没有做了~~:

似乎这个例子不是很好,但是够用来说明了。

Pull Members Up

开始之前让我们先看看Cal2类:

以及Cal2的父类Cal

最后的结果,就是将Cal2类中的sub方法,提到父类:

而我们所要做的就是鼠标右键

重构之以查询取代临时变量

快捷键

Mac: 木有

Windows/Linux: 木有

或者: Shift+alt+command+T 再选择 Replace Temp with Query

鼠标: Refactor | Replace Temp with Query

重构之前

过多的临时变量会让我们写出更长的函数,函数不应该太多,以便使功能单一。这也是重构的另外的目的所在,只有函数专注于其功能,才会更容易读懂。

以书中的代码为例

重构

选中basePrice很愉快地拿鼠标点上面的重构

Replace Temp With Query
Replace Temp With Query

便会返回

而实际上我们也可以

  1. 选中

    _quantity * _itemPrice

  2. 对其进行Extrace Method

  3. 选择basePriceInline Method

Intellij IDEA重构

在Intellij IDEA的文档中对此是这样的例子

接着我们选中aString,再打开重构菜单,或者

Command+Alt+Shift+T 再选中Replace Temp with Query

便会有下面的结果:

import java.lang.String;

public class replaceTemp {

    public void method() {
        String str = "str";
        System.out.println(aString(str));
    }

    private String aString(String str) {
        return returnString().concat(str);
    }

}

改善 GitHub 项目代码质量:测试

TDD

虽然接触的TDD时间不算短,然而真正在实践TDD上的时候少之又少。除去怎么教人TDD,就是与人结对编程时的switch,或许是受限于当前的开发流程。

偶然间在开发一个物联网相关的开源项目——Lan的时候,重拾了这个过程。不得不说提到的一点是,在我们的开发流程中测试是由相关功能开发人员写的,有时候测试是一种很具挑战性的工作。久而久之,为自己的开源项目写测试变成一种自然而然的事。有时没有测试,反而变得没有安全感

一次测试驱动开发

之前正在重写一个物联网的服务端,主要便是结合CoAP、MQTT、HTTP等协议构成一个物联网的云服务。现在,主要的任务是集中于协议与授权。由于,不同协议间的授权是不一样的,最开始的时候我先写了一个http put授权的功能,而在起先的时候是如何测试的呢?

curl --user root:root -X PUT -d '{ "dream": 1 }' -H "Content-Type: application/json" http://localhost:8899/topics/test

我只要顺利在request中看有无req.headers.authorization,我便可以继续往下,接着给个判断。毕竟,我们对HTTP协议还是蛮清楚的。

可是除了HTTP协议,还有MQTT和CoAP。对于MQTT协议来说,那还算好,毕竟自带授权,如:

便可以让我们简单地完成这个功能,然而有的协议是没有这样的功能如CoAP协议中是用Option来进行授权的。现在的工具如libcoap只能有如下的简单功能

于是,先写了个测试脚本来验证功能。

写完测试脚本后发现不对了,这个不应该是测试的代码吗? 于是将其放到了spec中,接着发现了上面的全部功能的实现过程为什么不用TDD实现呢?

说说TDD

测试驱动开发是一个很“古老”的程序开发方法,然而由于国内的开发流程的问题——即开发人员负责功能的测试,导致这么好的一项技术没有在国内推广。

测试驱动开发的主要过程是:

  1. 先写功能的测试
  2. 实现功能代码
  3. 提交代码(commit -> 保证功能正常)
  4. 重构功能代码

而对于这样的一个物联网项目来说,我已经有了几个有利的前提:

  1. 已经有了原型
  2. 框架设计

TDD思考

通常在我的理解下,TDD是可有可无的。既然我知道了我要实现的大部分功能,而且我也知道如何实现。与此同时,对Code Smell也保持着警惕、要保证功能被测试覆盖。那么,总的来说TDD带来的价值并不大。

然而,在当前这种情况下,我知道我想要的功能,但是我并不理解其深层次的功能。我需要花费大量的时候来理解,它为什么是这样的,需要先有一些脚本来知道它是怎么工作的。TDD变显得很有价值,换句话来说,在现有的情况下,TDD对于我们不了解的一些事情,可以驱动出更多的开发。毕竟在我们完成测试脚本之后,我们也会发现这些测试脚本成为了代码的一部分。

在这种理想的情况下,我们为什么不TDD呢?

功能测试

轻量级网站测试TWill

twill was initially designed for testing Web sites, although since then people have also figured out that it’s good for browsing unsuspecting Web sites.

之所以说轻量的原因是他是拿命令行测试的,还有DSL,还有Python。

除此之外,还可以拿它做压力测试,这种压力测试和一般的不一样。可以模拟整个过程,比如同时有多少人登陆你的网站。

不过,它有一个限制是没有JavaScript。

看了一下源码,大概原理就是用requests下载html,接着用lxml解析html,比较有意思的是内嵌了一个DSL

这是一个Python的库。

 pip install twill

Twill 登陆测试

1.启动我们的应用。

2.进入twill shell

twill-sh
 -= Welcome to twill! =-
current page:  *empty page*

3.打开网页

>> go http://127.0.0.1:5000/login
==> at http://127.0.0.1:5000/login
current page: http://127.0.0.1:5000/login

4.显示表单

    >> showforms

Form #1
## ## __Name__________________ __Type___ __ID________ __Value__________________
1     csrf_token               hidden    csrf_token   1423387196##5005bdf3496e09b8e2fbf450 ...
2     email                    email     email        None
3     password                 password  password     None
4     login                    submit    (None)       登入

current page: http://127.0.0.1:5000/login

5.填充表单

formclear 1
fv 1 email test@tes.com
fv 1 password test

6.修改action

formaction 1 http://127.0.0.1:5000/login

7.提交表单

>> submit
Note: submit is using submit button: name="login", value="登入"
current page: http://127.0.0.1:5000/

发现重定向到首页了。

Twill 测试脚本

当然我们也可以用脚本直接来测试login.twill:

go http://127.0.0.1:5000/login

showforms
formclear 1
fv 1 email test@tes.com
fv 1 password test
formaction 1 http://127.0.0.1:5000/login
submit

go http://127.0.0.1:5000/logout

运行

 twill-sh login.twill

结果

>> EXECUTING FILE login.twill
AT LINE: login.twill:0
==> at http://127.0.0.1:5000/login
AT LINE: login.twill:2

Form #1
## ## __Name__________________ __Type___ __ID________ __Value__________________
1     csrf_token               hidden    csrf_token   1423387345##7a000b612fef39aceab5ca54 ...
2     email                    email     email        None
3     password                 password  password     None
4     login                    submit    (None)       登入

AT LINE: login.twill:3
AT LINE: login.twill:4
AT LINE: login.twill:5
AT LINE: login.twill:6
Setting action for form  (<Element form at 0x10e7cbb50>,) to  ('http://127.0.0.1:5000/login',)
AT LINE: login.twill:7
Note: submit is using submit button: name="login", value="登入"

AT LINE: login.twill:9
==> at http://127.0.0.1:5000/login
--
1 of 1 files SUCCEEDED.

一个成功的测试诞生了。

Fake Server

实践了一下怎么用sinon去fake server,还没用respondWith,于是写一下。

这里需要用到sinon框架来测试。

当我们fetch的时候,我们就可以返回我们想要fake的结果。

    var data = {"id":1,"name":"Rice","type":"Good","price":12,"quantity":1,"description":"Made in China"};
beforeEach(function() {
    this.server = sinon.fakeServer.create();
    this.rices = new Rices();
    this.server.respondWith(
        "GET",
        "http://localhost:8080/all/rice",
        [
            200,
            {"Content-Type": "application/json"},
            JSON.stringify(data)
        ]
    );
});

于是在 afterEach 的时候,我们需要恢复这个server。

afterEach(function() {
    this.server.restore();
});

接着写一个jasmine测试来测试

describe("Collection Test", function() {
    it("should get data from the url", function() {
        this.rices.fetch();
        this.server.respond();
        var result = JSON.parse(JSON.stringify(this.rices.models[0]));
        expect(result["id"])
            .toEqual(1);
        expect(result["price"])
            .toEqual(12);
        expect(result)
            .toEqual(data);
    });
});

如何推广

除了擅长编写 md 电子书来攒 star,我还写了一系列的开源软件,也掌握了一些项目运营的技巧。

开源并不是你把软件、README 写好就行了,还有详细的文档、示例程序等等

开源也不是你的项目好了,就会有一堆人参与进来

开源还要你帮助别人解决 Bug,……

人们做事都是有原因的,即动机。再举例一下,如果你的项目不够火,别人都没听过,那么写到简历上可能没啥用

Marketing First

开源需要一些营销的技巧,这些技巧可以帮你吸引关注。举个简单的例子,司徒正美的 avalon 框架出身得很早,也 MV* 方面也做得很不错,但是在 marketing 上就……。以至于国内的很多前端,都不了解这个框架,要不今天在国内可能就是 AVRR 四大框架了。

Vue 不是因为好用,而一下子火了。这一点我印象特别深,当时在 GitHub Trending 上看到了这个项目,发现它还不能很好地 work。

而如文章 《FIRST WEEK OF LAUNCHING VUE.JS》所说,项目刚开始的时候作者做了一系列的营销计划:

除此,文中还提到了一篇文章《How to Spread The Word About Your Code》 。

这一点相当的有意思,如果你的想法好的话,那么大家都会肯定,点下链接,为你来个 star。那么,你就获得更好的动力去做这件事。项目也在开头的时候,获得了相当多的关注。而如果大家觉得你的项目没有新意的话,那么你懂的~。

除此,还有一种可能是,你的 ID 不够 fancy,即你在社区的影响上比较少。此时,就需要一点点慢慢积累人气了。当你积累了一些人气,你就能和松本行弘一样,在创建 mRuby 的时候就有 1000+ 的 star。并且,在社区上还有一些相关的文章介绍,各个头条也由他的粉丝发了上去。如,一年多以前,我创建了 mole 项目。

Mole
Mole

当时,是为了给自己做一个基于 GitHub 云笔记的工具,在完成度到一定程度的时候。我在我的微信公从号上发了相关的介绍,第二天就有 100+ 的 star 了,还接收至最一些鼓舞的话语。对应于国内则有:

所以,你觉得呢?

编写一个好的 README

在一个开源项目里,README 是最重要的内容。它快速地介绍了这个项目,并决定了它能不能吸引用户:

这个项目做什么——一句话文案

GitHub 的 Description 是我们在 Hacking News、GitHub Trneding 等等,第一时间看到的介绍。也是我们能快速介绍给别人的东西,如下图所示:

GitHub Trending
GitHub Trending

这一句话,必须简单明了也介绍,它是干什么的。

如 Angular 的一句话方案是:One framework. Mobile & desktop.

而 React 是:A declarative, efficient, and flexible JavaScript library for building user interfaces.

Vue 则是:A progressive, incrementally-adoptable JavaScript framework for building UI on the web.

它解决了什么问题

上面的一句话描述,它不能很好地说明,它能解决什么问题。

如下是今天在 GitHub Trending 上榜的 RPC 项目的简介:

Most machines on internet communicate with each other via TCP/IP. However TCP/IP only guarantees reliable data transmissions, we need to abstract more to build services:

RPC 开源项目
RPC 开源项目

以上便是这个项目能解决的问题,不过这个项目能解决的问题倒是比较长,哈哈哈。

它有什么特性

当我们有 A、B、C 几个不同的框架的时候,作为一个开发人员,就需要对比他们的特性,。如下是 Go 语言实现的 MQTT 示例:

GO MQTT 示例
GO MQTT 示例

这个项目只支持的 Qos 级别为 0。如果我们需要的级别是 1,那么就不能用这个项目了。

又比如 lodash 项目:

Lodash makes JavaScript easier by taking the hassle out of working with arrays, numbers, objects, strings, etc. Lodash’s modular methods are great for:

你会怎么写?脸皮够厚的话,可以直接写一下,与其它项目的对比,blabla:

对比其它项目
对比其它项目

当然了,这种事不能太过,要不然会招来一堆黑。

安装及hello, world 示例

在我们看完了上面的介绍之后,紧接着接一个 hello, world 的示例。在运行 hello, world 之前,我们可能需要一些额外的安装工作,如:

npm install koa

如 Koa 的示例:

const Koa = require('koa');
const app = new Koa();

// response
app.use(ctx => {
  ctx.body = 'Hello Koa';
});

app.listen(3000);

作为一个程序员,你应该懂得它的重要性。

好在这里的安装工作只有两步,而不是:

Lan 安装过程
Lan 安装过程

对于那些需要复杂的安装过程的软件,应该简化安装过程,如提供 Docker 镜像,或者直接提供一个可运行的 Demo 环境。以免用户在看完 README 之后,直接放弃了使用该库。

技术文档

好了,依一个开发人员的角度,如果上面的步骤一切顺利的话,接下来,便是使用这个开源项目来完成我们的功能。这个时候,我们开始转移注意力到文档上了。

由于,之前在某一个项目,经历过一个第三方 API 文档的大坑——文档中只罗列了 API 的用法。如下 Intellij Idea 生成的结构图:

API 示例
API 示例

文档中上,罗列了每个函数,以及每个函数需要的参数。我使用 Intellij Idea 直接反编译 jar 包,看到的信息都比文档多多了。文档上,没有任务示例,甚至连怎么初始化这个库的代码都没有。

WTF!

技术文档

对于一个复杂的开源项目来说,文档上要写明安装、编译、配置等等的过程。如下图所示:

Python Social Auth 文档
Python Social Auth 文档

并且在我们发布包的时候,就要不断地去重复这个过程——如果你使用了自动化测试,那么这个过程便自动完成了。

如果我们的项目使用起来相当的简单,那么我们就可以只写一些示例代码即可。

并且,我们可以将文档直接入到代码里。它可以有效地减少文档不同步,带来的一些问题。

Lodash 示例
Lodash 示例

上图是使用了 jsdoc 的 Lodash 示例。

除了上面的示例,我们还可以录制一些视频,写一些文章说明项目的思考、架构等等。

更多的示例程序

示例代码本身也是文档的一部分,不要问我为什么~~。

反正,除了一个 hello, world,你还要有各种场景下的示例:

Redux
Redux

没有这么多示例,敢说自己是好用的开源项目?

编写技术文章、书籍

到目前为止,我们做了一系列 markdown 相关的工作,却也还没有结束。要知道只有真正写过一系列开源项目的人,才能体会到什么是 markdown 程序员~~。

官方文档,一般要以比较正式的口吻来描述过程,这种写法相当的压抑。如果要用轻松诙谐的口气,那么就可以写一系列的技术文章。假如这是一个前端框架,那么我们可以介绍它如何与某个后端框架配合使用;又或者是,它与某个框架的对比等等。

好了,一切顺利了,那么下一步就是吸引更多的人参与到项目上来了。

鼓励、吸引贡献者

要吸引其它开发人员来到你的项目,不是一件容易的事。

你需要不断地鼓励他/她们,并适时地帮他/她们解决问题,以避免他/她们在提 pull request 的过程中放弃了。这一点特别的有意思,当有一个开发人员发现了项目中的 bug,那么他/她会尝试去解决这个问题。与此同时,他/她还会为你的项目带来 pull request,但是在这个过程中,因为测试等等的问题,可能会阻碍他的 PR。这个时候,就需要我们主要去提示/教他们怎么做,又或者是帮他/她们解决完剩下的问题。那么,下次他/她提一个 PR 的时候,他/她就能解决问题了。

这一点可以在 README,以及介绍上体现:

Feel free to contribute!
Feel free to contribute!

哪怕只是一个错误字的 PR,那么你也可以 merge,啊哈哈~。然后,就有人帮你宣传了,『我给 xxx 项目一个 PR 了』。刚毕业的时候,我也是从这种类型的 PR 做起的~~。

开源项目维护

Release

Git 与 GitHub 工具推荐

Git 命令行增强

diff-so-fancy

diff so fancy 截图
diff so fancy 截图

git-extras

Ubuntu

$ sudo apt-get install git-extras

Mac OS X with Homebrew

$ brew install git-extras
$ git-summary


 project  : github-roam
 repo age : 2 years, 7 months
 active   : 40 days
 commits  : 124
 files    : 101
 authors  :
    72  Fengda HUANG  58.1%
    29  Fengda Huang  23.4%
     8  Phodal HUANG  6.5%
     3  Phodal Huang  2.4%
     2  yangpei3720   1.6%
     2  WangXiaolong  1.6%
     2  TZS           1.6%
     1  安正超        0.8%
     1  Li            0.8%
     1  Qiuer         0.8%
     1  SCaffrey      0.8%
     1  oncealong     0.8%
     1  zminds        0.8%

Intellij IDEA

由于日常用的开发工是 Intellij IDEA 企业版,所以就有点依赖于这个工具了。最常用的功能便是:修复 Bug 时,对于文件修改的追溯。了解某行代码修改的原因,对应的修改人等等。

Intellij IDEA

Git、GitHub桌面增强

SourceTree

SourceTree 方便用来查看一些非我写的项目,寻找其中的一些问题。个中缘由便是:Intelli IDEA 刚打开某个项目的时候,需要花费大量的时间 index,只可惜现有的 SourceTree 客户端都需要登录 Atlassian 账户了。

gitflow 分支合并、查看

SourceTree 截图
SourceTree 截图

GitHub Desktop

GitHub Desktop
GitHub Desktop

Git 娱乐

githug

$ githug 

********************************************************************************
*                                    Githug                                    *
********************************************************************************
No githug directory found, do you wish to create one? [yn]  y
Welcome to Githug!

Name: init
Level: 1
Difficulty: *

A new directory, `git_hug`, has been created; initialize an empty repository in it.
$ githug play                                                                                              

********************************************************************************
*                                    Githug                                    *
********************************************************************************
Congratulations, you have solved the level!

Name: config
Level: 2
Difficulty: *

Set up your git name and email, this is important so that your commits can be identified.
#1: init
#2: config
#3: add
#4: commit
#5: clone
#6: clone_to_folder
#7: ignore
#8: include
#9: status
#10: number_of_files_committed
#11: rm
#12: rm_cached
#13: stash
#14: rename
#15: restructure
#16: log
#17: tag
#...

Gource

Gource 历史
Gource 历史

GitHub用户分析

生成图表

如何分析用户的数据是一个有趣的问题,特别是当我们有大量的数据的时候。除了matlab,我们还可以用numpy+matplotlib

数据可以在这边寻找到

https://github.com/gmszone/ml

最后效果图

2014 01 01
2014 01 01

要解析的json文件位于data/2014-01-01-0.json,大小6.6M,显然我们可能需要用每次只读一行的策略,这足以解释为什么诸如sublime打开的时候很慢,而现在我们只需要里面的json数据中的创建时间。。

==, 这个文件代表什么?

2014年1月1日零时到一时,用户在github上的操作,这里的用户指的是很多。。一共有4814条数据,从commit、create到issues都有。

数据解析

然后再解析json

这里用到了dateutil,因为新鲜出炉的数据是string需要转换为dateutil,再到数据放到数组里头。最后有就有了parse_data

下面这句代码就是将上面的解析为

这样的数组以便于解析

Matplotlib

开始之前需要安装``matplotlib

然后引入这个库

  import matplotlib.pyplot as plt

如上面的那个结果,只需要


    plt.figure(figsize=(8,4))
    plt.plot(x, y,label = files)
    plt.legend()
    plt.show()

最后代码可见

每周分析

继上篇之后,我们就可以分析用户的每周提交情况,以得出用户的真正的工具效率,每个程序员的工作时间可能是不一样的,如

Phodal Huang’s Report
Phodal Huang’s Report

这是我的每周情况,显然如果把星期六移到前面的话,随着工作时间的增长,在github上的使用在下降,作为一个

  a fulltime hacker who works best in the evening (around 8 pm).

不过这个是osrc的分析结果。

python github 每周情况分析

看一张分析后的结果

Feb Results
Feb Results

结果正好与我的情况相反?似乎图上是这么说的,但是数据上是这样的情况。

data
├── 2014-01-01-0.json
├── 2014-02-01-0.json
├── 2014-02-02-0.json
├── 2014-02-03-0.json
├── 2014-02-04-0.json
├── 2014-02-05-0.json
├── 2014-02-06-0.json
├── 2014-02-07-0.json
├── 2014-02-08-0.json
├── 2014-02-09-0.json
├── 2014-02-10-0.json
├── 2014-02-11-0.json
├── 2014-02-12-0.json
├── 2014-02-13-0.json
├── 2014-02-14-0.json
├── 2014-02-15-0.json
├── 2014-02-16-0.json
├── 2014-02-17-0.json
├── 2014-02-18-0.json
├── 2014-02-19-0.json
└── 2014-02-20-0.json

我们获取是每天晚上0点时的情况,至于为什么是0点,我想这里的数据量可能会比较少。除去1月1号的情况,就是上面的结果,在只有一周的情况时,总会以为因为在国内那时是假期,但是总觉得不是很靠谱,国内的程序员虽然很多,会在github上活跃的可能没有那么多,直至列出每一周的数据时。

  6570, 7420, 11274, 12073, 12160, 12378, 12897,
  8474, 7984, 12933, 13504, 13763, 13544, 12940,
  7119, 7346, 13412, 14008, 12555

Python 数据分析

重写了一个新的方法用于计算提交数,直至后面才意识到其实我们可以算行数就够了,但是方法上有点hack

接着我们需要去遍历每个结果,后面的后面会发现这个效率真的是太低了,为什么木有多线程?

Python Matplotlib图表

让我们的matplotlib来做这些图表的工作

蓝色的是第一周,绿色的是第二周,红色的是第三周就有了上面的结果。

我们还需要优化方法,以及多线程的支持。

让我们分析之前的程序,然后再想办法做出优化。网上看到一篇文章http://www.huyng.com/posts/python-performance-analysis/讲的就是分析这部分内容的。

存储到数据库中

SQLite3

我们创建了一个名为userdata.db的数据库文件,然后创建了一个表,里面有owner,language,eventtype,name url

接着我们就可以查询数据,这里从结果讲起。

当我查询gmszone的时候,也就是我自己就会有如下的结果

一共有109个事件,有Watch,Create,Push,Fork还有其他的, 项目主要有iot,RESUME,iot-dashboard,wechat-wordpress, 接着就是语言了,Tex,Javascript,Ruby,接着就是项目的url了。

值得注意的是。

这个数据库文件有905M,不过查询结果相当让人满意,至少相对于原来的结果来说。

Python自带了对SQLite3的支持,然而我们还需要安装SQLite3

或者是

或者是Ubuntu的

openSUSE自然就是

不过,用yast2也很不错,不是么。。

数据导入

需要注意的是这里是需要python2.7,起源于对gzip的上下文管理器的支持问题

executemany可以插入多条数据,对于我们的数据来说,一小时的文件大概有五六千个会符合我们上面的安装,也就是有actor又有type才是我们需要记录的数据,我们只需要统计用户的那些事件,而非全部的事件。

我们需要去遍历文件,然后找到合适的部分,这里只是要找2014-03-012014-03-31的全部事件,而光这些数据的gz文件就有1.26G,同上面那些解压为json文件显得不合适,只能用遍历来处理。

这里参考了osrc项目中的写法,或者说直接复制过来。

首先是正规匹配

不过主要的还是在于glob.glob

glob是python自己带的一个文件操作相关模块,用它可以查找符合自己目的的文件,就类似于Windows下的文件搜索,支持通配符操作。

这里也就用上了gzip.GzipFile又一个不错的东西。

最后代码可以见

github.com/gmszone/ml

更好的方案?

Redis

查询用户事件总数

系统返回了227.0,试试别人。

看看主要是在哪一天提交的

结果大致如下图所示:

SMTWTFS
SMTWTFS

看看主要的事件是?

>>> pipe.zrevrange("osrc:user:gmszone:event".format("gmszone"), 0, -1,withscores=True)
<redis.client.StrictPipeline object at 0x104fa7f50>
>>> pipe.execute()
[[('PushEvent', 154.0), ('CreateEvent', 41.0), ('WatchEvent', 18.0), ('GollumEvent', 8.0), ('MemberEvent', 3.0), ('ForkEvent', 2.0), ('ReleaseEvent', 1.0)]]
>>>
Main Event
Main Event

蓝色的就是push事件,黄色的是create等等。

到这里我们算是知道了OSRC的数据库部分是如何工作的。

Redis 查询

主要代码如下所示

结果在上一篇中显示出来了,也就是

[227.0, {'1': '51', '0': '41', '3': '17', '2': '34', '5': '28', '4': '22', '6': '34'}, [('PushEvent', 154.0), ('CreateEvent', 41.0), ('WatchEvent', 18.0), ('GollumEvent', 8.0), ('MemberEvent', 3.0), ('ForkEvent', 2.0), ('ReleaseEvent', 1.0)], 0, 0, 0, 11, [('CSS', 74.0), ('JavaScript', 60.0), ('Ruby', 12.0), ('TeX', 6.0), ('Python', 6.0), ('Java', 5.0), ('C++', 5.0), ('Assembly', 5.0), ('C', 3.0), ('Emacs Lisp', 2.0), ('Arduino', 2.0)]]

有意思的是在这里生成了和自己相近的人

['alesdokshanin', 'hjiawei', 'andrewreedy', 'christj6', '1995eaton']

osrc最有意思的一部分莫过于flann,当然说的也是系统后台的设计的一个很关键及有意思的部分。

邻近算法与相似用户

邻近算法是在这个分析过程中一个很有意思的东西。

邻近算法,或者说K最近邻(kNN,k-NearestNeighbor)分类算法可以说是整个数据挖掘分类技术中最简单的方法了。所谓K最近邻,就是k个最近的邻居的意思,说的是每个样本都可以用她最接近的k个邻居来代表。

换句话说,我们需要一些样本来当作我们的分析资料,这里东西用到的就是我们之前的。

[227.0, {'1': '51', '0': '41', '3': '17', '2': '34', '5': '28', '4': '22', '6': '34'}, [('PushEvent', 154.0), ('CreateEvent', 41.0), ('WatchEvent', 18.0), ('GollumEvent', 8.0), ('MemberEvent', 3.0), ('ForkEvent', 2.0), ('ReleaseEvent', 1.0)], 0, 0, 0, 11, [('CSS', 74.0), ('JavaScript', 60.0), ('Ruby', 12.0), ('TeX', 6.0), ('Python', 6.0), ('Java', 5.0), ('C++', 5.0), ('Assembly', 5.0), ('C', 3.0), ('Emacs Lisp', 2.0), ('Arduino', 2.0)]]

在代码中是构建了一个points.h5的文件来分析每个用户的points,之后再记录到hdf5文件中。

[ 0.00438596  0.18061674  0.2246696   0.14977974  0.07488987  0.0969163
    0.12334802  0.14977974  0.          0.18061674  0.          0.          0.
    0.00881057  0.          0.          0.03524229  0.          0.
    0.01321586  0.          0.          0.          0.6784141   0.
    0.07929515  0.00440529  1.          1.          1.          0.08333333
    0.26431718  0.02202643  0.05286344  0.02643172  0.          0.01321586
    0.02202643  0.          0.          0.          0.          0.          0.
    0.          0.          0.00881057  0.          0.          0.          0.
    0.          0.          0.          0.          0.          0.          0.
    0.          0.          0.          0.          0.00881057]

这里分析到用户的大部分行为,再找到与其行为相近的用户,主要的行为有下面这些:

osrc中用于解析的代码

这样也就返回我们需要的点数,然后我们可以用get_points来获取这些

就会得到我们的相应的数据,接着找找和自己邻近的,看看结果。

[ 0.01298701  0.19736842  0.          0.30263158  0.21052632  0.19736842
    0.          0.09210526  0.          0.22368421  0.01315789  0.          0.
    0.          0.          0.          0.01315789  0.          0.
    0.01315789  0.          0.          0.          0.73684211  0.          0.
    0.          1.          1.          1.          0.2         0.42105263
    0.09210526  0.          0.          0.          0.          0.23684211
    0.          0.          0.03947368  0.          0.          0.          0.
    0.          0.          0.          0.          0.          0.          0.
    0.          0.          0.          0.          0.          0.          0.
    0.          0.          0.          0.        ]

真看不出来两者有什么相似的地方 。。。。

如何在GitHub“寻找灵感(fork)”

重造轮子是重新创造一个已有的或是已被其他人优化的基本方法。

最近萌发了一个想法写游戏引擎,之前想着做一个JavaScript前端框架。看看,这个思路是怎么来的。

Lettuce构建过程

Lettuce是一个简约的移动开发框架。

故事的出发点是这样的:写了很多代码,用的都是框架,最后不知道收获什么了?事实也是如此,当自己做了一些项目之后,发现最后什么也没有收获到。于是,就想着做一个框架。

需求

有这样的几个前提

我想要的只是一个简单的功能,而我不想引入一个庞大的库。换句话说,我只需要不同库里面的一小部分功能,而不是一个库。

实际上想要的是:

构建一个库,里面从不同的库里面抽取出不同的函数。

计划

这时候我参考了一本电子书《Build JavaScript FrameWork》,加上一些平时的需求,于是很快的就知道自己需要什么样的功能:

在做一些实际的项目中,还遇到了这样的一些功能支持:

而我们有一个前提是要保持这个库尽可能的小、同时我们还需要有测试。

实现第一个需求

简单说说是如何实现一个简单的需求。

生成框架

因为Yeoman可以生成一个简单的轮廓,所以我们可以用它来生成这个项目的骨架。

寻找

在GitHub上搜索了一个看到了下面的几个结果:

但是显然,他们都太重了。事实上,对于一个库来说,80%的人只需要其中20%的代码。于是,找到了https://github.com/stackp/promisejs,看了看用法,这就是我们需要的功能:

接着打开看看Promise对象,有我们需要的功能,但是又有一些功能超出我的需求。接着把自己不需要的需求去掉,这里函数最后就变成了

需要注意的是: License,不同的软件有不同的License,如MIT、GPL等等。最好能在遵循协议的情况下,使用别人的代码。

实现第二个需求

由于已经有了现有的很多库,所以就可以直接参照(抄)别人写的代码。

如何以“正确的姿势”阅读开源软件代码

所有让你直接看最新源码的文章都是在扯淡,你应该从“某个版本”开始阅读代码。

我们并不建议所有的读者都直接看最新的代码,正确的姿势应该是:

最好的在这个过程中,可以自己造轮子来实现一遍

阅读过程

在我阅读的前端库、Python后台库的过程中,我们都是以造轮子为目的展开的。所以在最开始的时候,我需要一个可以工作,并且拥有我想要的功能的版本。

it-works-cms.png
it-works-cms.png

紧接着,我就可以开始去实践这个版本中的一些功能,并理解他们是怎么工作的。再用git大法展开之前修改的内容,可以使用IDE自带的Diff工具:

pycharm-diff.jpg
pycharm-diff.jpg

或者类似于SourceTree这样的工具,来查看修改的内容。

在我们理解了基本的核心功能后,我们就可以向后查看大、中版本的更新内容了。

开始之前,我们希望大家对版本号管理有一些基本的认识。 ## 版本号管理

我最早阅读的开始软件是Linux,而下面则是Linux的Release过程:

linux-history.png
linux-history.png

表格源自一本书叫《Linux内核0.11(0.95)完全注释》,简单地再介绍一下:

这里就要扯到《GNU 风格的版本号管理策略》:

  1. 项目初版本时,版本号可以为 0.1 或 0.1.0, 也可以为 1.0 或 1.0.0,如果你为人很低调,我想你会选择那个主版本号为 0 的方式;
  2. 当项目在进行了局部修改或 bug 修正时,主版本号和子版本号都不变,修正版本号加 1;
  3. 当项目在原有的基础上增加了部分功能时,主版本号不变,子版本号加 1,修正版本号复位为 0,因而可以被忽略掉;
  4. 当项目在进行了重大修改或局部修正累积较多,而导致项目整体发生全局变化时,主版本号加 1;
  5. 另外,编译版本号一般是编译器在编译过程中自动生成的,我们只定义其格式,并不进行人为控制。

因此,我们可以得到几个简单的结论:

示例

以Flask为例:

一、先Clone它。

clone-flask.png
clone-flask.png

二、从Release页面找到它的早期版本:

flask.png
flask.png

三、 从上面拿到它的提交号8605cc3,然后checkout到这次提交,查看功能。在这个版本里,一共有六百多行代码

flask-0.1.png
flask-0.1.png

还是有点长

四、我们可以找到它的最早版本:

flask-init.png
flask-init.png

然后查看它的flask.py文件,只有简单的三百多行,并且还包含一系列注释:

flask-init.png
flask-init.png

五、接着,再回过头去阅读

GitHub连击

100天

我也是蛮拼的,虽然我想的只是在GitHub上连击100~200天,然而到了今天也算不错。

Longest Streak
Longest Streak

在不停地造轮子的过程中,也不停地造车子。

在那篇连续冲击365天的文章出现之前,我们公司的大大(https://github.com/dreamhead)也曾经在公司内部说过,天天commit什么的。当然这不是我的动力,在连击140天之前

对比了一下365天连击的commit,我发现我在total上整整多了近0.5倍。

365 Streak
365 Streak

同时这似乎也意味着,我每天的commit数与之相比多了很多。

在连击20的时候,有这样的问题: 为了commit而commit代码,最后就放弃了。

而现在是为了填坑而commit,为自己挖了太多的想法。

40天的提升

当时我需要去印度接受毕业生培训,大概有5周左右,想着总不能空手而归。于是在国庆结束后有了第一次commit,当时旅游归来,想着自己在不同的地方有不同的照片,于是这个repo的名字是 onmap——将自己的照片显示在地图上的拍摄地点(手机是Lumia 920)。然而,中间因为修改账号的原因,丢失了commit。

再从印度说起,当时主要维护三个repo:

说说最后一个,最后一个是练习的项目。因为当时培训比较无聊,业余时间比较多,英语不好,加上听不懂印度人的话。晚上基本上是在住的地方默默地写代码,所以当时的目标有这么几个:

这也就是为什么那个repo有这样的一行:

Repo Status
Repo Status

做到98%的覆盖率也算蛮拼的,当然还有Code Climate也达到了4.0,也有了112个commits。因此也带来了一些提高:

100天的挑战

(ps:从印度回来之后,由于女朋友在泰国实习,有了更多的时间可以看书、写代码)

有意思的是越到中间的一些时间,commits的次数上去了,除了一些简单的pull request,还有一些新的轮子出现了。

Problem
Problem

这是上一星期的commits,这也就意味着,在一星期里面,我需要在8个repo里切换。而现在我又有了一个新的idea,这时就发现了一堆的问题:

140天的希冀

在经历了100天之后,似乎整个人都轻松了,毕竟目标是100~200天。似乎到现在,也不会有什么特殊的情怀,除了一些希冀。

当然,对于一个开源项目的作者来说,最好有下面的情况:

200天的Showcase

今天是我连续泡在GitHub上的第200天,也是蛮高兴的,终于到达了:

GitHub 200 days
GitHub 200 days

故事的背影是: 去年国庆完后要去印度接受毕业生培训——就是那个神奇的国度。但是在去之前已经在项目待了九个多月,项目上的挑战越来越少,在印度的时间又算是比较多。便给自己设定了一个长期的goal,即100~200天的longest streak。

或许之前你看到过一篇文章让我们连击,那时已然140天,只是还是浑浑噩噩。到了今天,渐渐有了一个更清晰地思路。

先让我们来一下ShowCase,然后再然后,下一篇我们再继续。

一些项目简述

上面说到的培训一开始是用Java写的一个网站,有自动测试、CI、CD等等。由于是内部组队培训,代码不能公开等等因素,加之做得无聊。顺手,拿Node.js +RESTify 做了Server,Backbone + RequireJS + jQuery 做了前台的逻辑。于是在那个日子里,也在维护一些旧的repo,如iot-coapiot,前者是我拿到WebStorm开源License的Repo,后者则是毕业设计。

对于这样一个项目也需要有测试、自动化测试、CI等等。CI用的是Travics-CI。总体的技术构架如下:

技术栈

前台:

后台:

测试:

一直写到五星期的培训结束, 只是没有自动部署。想想就觉得可以用github-page的项目多好~~。

过程中还有一些有意思的小项目,如:

google map solr polygon 搜索

google map solr polygon 搜索

google map solr
google map solr

代码: https://github.com/phodal/gmap-solr

技能树

这个可以从两部分说起:

重构 Skill Tree

原来的是

Skill Tree
Skill Tree

代码: https://github.com/phodal/skillock

技能树Sherlock

Sherlock skill tree
Sherlock skill tree

代码: https://github.com/phodal/sherlock

Django Ionic ElasticSearch 地图搜索

Django Elastic Search
Django Elastic Search

代码: https://github.com/phodal/django-elasticsearch

简历生成器

Resume
Resume

代码: https://github.com/phodal/resume

Nginx 大数据学习

Nginx Pig
Nginx Pig

代码: https://github.com/phodal/learning-data/tree/master/nginx

其他

虽然技术栈上主要集中在Python、JavaScript,当然还有一些Ruby、Pig、Shell、Java的代码,只是我还是习惯用Python和JavaScript。一些用到觉得不错的框架:

还应该有:

365天

给你一年的时间,你会怎样去提高你的水平???

GitHub 365
GitHub 365

正值这难得的sick leave(万恶的空气),码文一篇来记念一个过去的366天里。尽管想的是在今年里写一个可持续的开源框架,但是到底这依赖于一个好的idea。在我的GitHub 孵化器 页面上似乎也没有一个特别让我满意的想法,虽然上面有各种不样有意思的ideas。多数都是在过去的一年是完成的,然而有一些也是还没有做到的。

尽管一直在GitHub上连击看上去似乎是没有多大必要的,但是人总得有点追求。如果正是漫无目的,却又想着提高技术的同时,为什么不去试试?毕竟技术非常好、不需要太多练习的人只是少数,似乎这样的人是不存在的。大多数的人都是经过练习之后,才会达到别人口中的“技术好”。

这让我想起了充斥着各种气味的知乎上的一些问题,在一些智商被完虐的话题里,无一不是因为那些人学得比别人早——哪来的天才?所谓的天才,应该是未来的智能生命一般,一出生什么都知道。如果并非如此,那只是说明他练习到位了。

练习不到位便意味着,即使你练习的时候是一万小时的两倍,那也是无济于事的。如果你学得比别人晚,在很长的一段时间里(可能直到进棺材)输给别人是必然的——落后就要挨打。就好像我等毕业于一所二本垫底的学校里,如果在过去我一直保持着和别人(各种重点)一样的学习速度,那么我只能一直是Loser。

需要注意的是,对你来说考上二本很难,并不是因为你比别人笨。教育资源分配不均的问题,在某种程度上导致了新的阶级制度的出现。如我的首页说的那样: THE ONLY FAIR IS NOT FAIR——唯一公平的是它是不公平的。我们可以做的还有很多——CREATE & SHARE。真正的不幸是,因为营养不良导致的教育问题。

于是在想明白了很多事的时候起,便有了Re-Practise这样的计划,而365天只是中间的一个产物。

编程的基础能力

虽说算法很重要,但是编码才是基础能力。算法与编程在某种程度上是不同的领域,算法编程是在编程上面的一级。算法写得再好,如果别人很难直接拿来复用,在别人眼里就是shit。想出能work的代码一件简单的事,学会对其重构,使之变得更易读就是一件有意义的事。

于是,在某一时刻在GitHub上创建了一个组织,叫Artisan Stack。当时想的是在GitHub寻找一些JavaScript项目,对其代码进行重构。但是到底是影响力不够哈,参与的人数比较少。

重构

如果你懂得如何写出高可读的代码,那么我想你是不需要这个的,但是这意味着你花了更多的时候在思考上了。当谈论重构的时候,让我想起了TDD(测试驱动开发)。即使不是TDD,那么如果你写着测试,那也是可以重构的。(之前写过一些利用Intellij IDEA重构的文章:提炼函数以查询取代临时变量重构与Intellij Idea初探内联函数)

在各种各样的文章里,我们看到过一些相关的内容,最好的参考莫过于《重构》一书。最基础不过的原则便是函数名,取名字很难,取别人能读懂的名字更难。其他的便有诸如长函数、过大的类、重复代码等等。在我有限的面试别人的经历里,这些问题都是最常见的。

测试

而如果没有测试,其他都是扯淡。写好测试很难,写个测试算是一件容易的事。只是有些容易我们会为了测试而测试。

在我写EchoesWorksLan的过程中,我尽量去保证足够高的测试覆盖率。

lan
lan
EchoesWorks
EchoesWorks

从测试开始的TDD,会保证方法是可测的。从功能到测试则可以提供工作次效率,但是只会让测试成为测试,而不是代码的一部分。

测试是代码的最后一公里。所以,尽可能的为你的GitHub上的项目添加测试。

编码的过程

初到TW时,Pair时候总会有人教我如何开始编码,这应该也是一项基础的能力。结合日常,重新演绎一下这个过程:

  1. 有一个可衡量、可实现、过程可测的目标
  2. Tasking (即对要实现的目标过程进行分解)
  3. 一步步实现 (如TDD)
  4. 实现目标

放到当前的场景就是:

  1. 我想在GitHub上连击365天。对应于每一个时候段的目标都应该是可以衡量、测试的——即每天都会有Contributions。
  2. 分解就是一个痛苦的过程。理想情况下,我们应该会有每天提交,但是这取决于你的repo的数量,如果没有新的idea出现,那么这个就变成为了Contributions而Commit。
  3. 一步步实现

在我们实际工作中也是如此,接到一个任务,然后分解,一步步完成。不过实现会稍微复杂一些,因为事务总会有抢占和优先级的。

技术与框架设计

在上上一篇博客中《After 500: 写了第500篇博客,然后呢?》也深刻地讨论了下这个问题,技术向来都是后发者优势。对于技术人员来说,也是如此,后发者占据很大的优势。

如果我们只是单纯地把我们的关注点仅仅放置于技术上,那么我们就不具有任何的优势。而依赖于我们的编程经验,我们可以在特定的时候创造一些框架。而架构的设计本身就是一件有意思的事,大抵是因为程序员都喜欢创造。(ps:之前曾经写过这样一篇文章,《对不起,我并不热爱编程,我只喜欢创造》)

创造是一种知识的再掌握过程。

回顾一下写echoesworks的过程,一开始我需要的是一个网页版的PPT,当然这类的东西已经有很多了,如impress.js、bespoke.js等等。分析一下所需要的功能:markdown解析器、键盘事件处理、Ajax、进度条显示、图片处理、Slide。我们可以在GitHub上找到各式各样的模块,我们所要做的就是将之结合在一样。在那之前,我试着用类似的原理写(组合)了Lettuce

组合相比于创造过程是一个更有挑战性的过程,我们需要在这过程去设计胶水来粘合这些代码,并在最终可以让他工作。这好比是我们在平时接触到的任务划分,每个人负责相应的模块,最后整合。

我在写lan的时候,也是类似的,但是不同的是我已经设计了一个清晰的架构图。

Lan IoT
Lan IoT

而在我们实现的编码过程也是如此,使用不同的框架,并且让他们能工作。如早期玩的moqi.mobi,基于Backbone、RequireJS、Underscore、Mustache、Pure CSS。在随后的时间里,用React替换了View层,就有了backbone-react的练习。

技术同人一样,需要不断地往高一级前进。我们只需要不断地Re-Practise。

领域与练习

说业务好像不太适合程序员的口味,那就领域吧。不同行业的人,如百度、阿里、腾讯,他们的领域核心是不一样的。

而领域本身也是相似的,这可以解释为什么互联网公司都喜欢互相挖人,而一般都不会去华为、中兴等非互联网领域挖人。出了这个领域,你可能连个毕业生都不如。领域、业务同技术一样是不断强化知识的一个过程。Ritchie先实现了BCPL语言,而后设计了C语言,而BCPL语言一开始是基于CPL语言。

领域本身也在不断进化。

这也是下一个值得提高的地方。

其他

是时候写这个小结了。从不会写代码,到写代码是从0到1的过程,但是要从1到60都不是一件容易的事。无论是刷GitHub也好(不要是自动提交),或者是换工作也好,我们都在不断地练习。

而练习是要分成不同的几个步骤,不仅仅局限于技术:

  1. 编码
  2. 架构
  3. 设计
  4. 。。。

500天

尽管之前已经有100天、200天、365天的文章,但是这不是一篇象征性的500天的文章。对这样的一个事物,每个人都会有不同听看法。有的会说这是一件好事,有的则不是。但是别人的看法终究不重要,因为了解你自己的只有你自己。别人都只是以他们的角度来提出观点。

在这500天里,我发现两点有意思的事,也是总结的时候才意识到的:

  1. 编程的情绪周期
  2. 有意图的练习

那么,当我们不断地练习的时候,我们就可以写出更好的代码。

我想你也听过一万小时天才理论的说法:要成为某个领域的专家,需要10000小时。而在这其中每重要的一点是有意图的练习——而不是一直重复性地用不同的语言去写一个相同的算法。如果我们有一天8小时的工作时间 + 2 小时的提高时间,那么我们还是需要1000天才能实现一万小时。

500天与10000小时

当然如果你连做梦也在写代码的话,那么我想500天就够了,哈哈~~。

Gtihub 500
Gtihub 500

虽然不是连击次数最多的,但是根据Most active GitHub users的结果来说,好似是大陆提交数最多的人,没有之一。再考虑到提交都是有意义的——不是机器刷出来的,不是有意识的去刷,我觉得还是有很大成就感的。

而要实现500天连击很重要的两点是:时间和idea。但是我觉得idea并不是非常重要的,我们可以造轮子,这一点就是在早期我做得最多的一件事,不断地造轮子——如《造轮子与从Github生成轮子》一文中所说。除此,你还可以用《GitHub去管理你的idea》,每当你想到一个Idea以及完成一个idea的时间你就会多一次提交。

时间则是一件很讽刺的事,因为人们要加班。加班的原因,要么是因为工作的内容很有意思,要么是因为钱。如果不是因为钱的话,为什么不去换个工作呢?比如我司。看似两者间存在很多的对立,但是我总在想技术的提升可以在后期解决收入的问题,而不需要靠加班来解决这个问题。人总是要活着的,钱是必需的,但是程序员的收入都不低。

编程的情绪周期

接着,我观察到了一些有意思的现象——编程的情绪周期也很明显。

所谓“情绪周期”,是指一个人的情绪高潮和低潮的交替过程所经历的时间。

如下图所示的就是情绪周期:

情绪周期
情绪周期

简单地来说,就是有一个时间段写代码的感觉超级爽,有一个时间段不想写代码,但是如果换一个说法就是:有一个时间段看书、写文档的感觉很爽,有一时间段不想看书、写文档的感觉。这也就是为什么在我的GitHub首页上的绿色各种花。不过因为《物联网周报》的原因,我会定期地更新一个相关的开源项目。

但是总来说,我习惯在一些时间造一些轮子、创建文档,这就是为什么我的GitHub会有一些开源电子书的缘故。

有意图的练习

编程需要很长的学习时间,也需要很长的练习时间。尽管我是从小学编程,自认为天赋不错,但是突破了上个门槛还是花费了三四年的时间。其中的很大一部分原因是,没有找对一个合适的方向。而在这期间也没有好好的练习,随后的日子里我意识到我会遇到下一个门槛,便开始试图有意识的练习。

在我开始工作的时候,我写了一篇名为《重新思考工作》的文章。在文章中我提到了几点练习的点:

在一些日子的练习后,我发现这还是太无聊了。天生就喜欢一些有意思的东西,有趣才更有激情吧~~。不过,像下图的打字练习还是挺有意思的:

打字练习
打字练习

还是能打出了一堆错误的字符。但是对比了一下大多数人的人,还算不错,至少是盲打。但是,还是存在着很大的提升空间。

随后,我开始一些错误的练习,如对设计模式和架构的练习。试图去练习一些在生产上用不到的设计模式,以及一些架构模式。而这时就意味着,需要生搬一些设计模式。最后,我开始以项目为目的的练习,这就是为什么我的GitHub上的提交数会有如此多的原因。

预见性练习

还有一种练习比较有意思,算是以工作为导向的练习。当我们预见到我们的项目需要某一些技术,我们可能在未来采用某些技术的时候,我们就需要开始预见性的练习这些技术。

好的一点是:这些项目可能在未来很受初学者欢迎。

小结

每个人都有自己的方向,都有一个不错的发展路线,分享和创造都是不错的路。

THE ONLY FAIR IS NOT FAIR . ENJOY CREATE & SHARE.

365*2-7天里

刚毕业的时候,有一段时间我一直困惑于如何去提高编码能力——因为项目上做的东西多数时候和自己想要的是不一样的,我便想着自己去找一些有意思的东西做着玩,在这个过程中边练习技能。

如果你知道自己代码能力不够,为什么不花两年时间去提高这方面的能力呢?

编码的练习

编码是一件值得练习的事,你从书中、互联网上看到的那一个个的编程大牛无一不是从一点点的小技能积累起来的。从小接触可以让你有一个好的开始,一段好好的练习也会帮助你更好的前进。

记得我在最开始练习的时候,我分几个不同的阶段去练习:

而这些并不是一种容易的事,很多时候有一些模式,我们都很难有一个好的实践。只是这些东西都不是一些可以生搬硬套的,我们更需要的是知道有这些东西的存在,以便于在某一天,我们可以从我们的仓库里将这些知识取出来。

10000 hours
10000 hours

我们的刻意练习加上我们的持之以恒总是会取得长足的进步。不过在我们练习之前,你需要有一个目标。这个目标可以是一个 Idea、一个设计模式、一个模仿等等,这些内容都可以以 Issue 的好好管理着。

在最开始我们下定目标的几天里,我们可以很容易做到这样的事。同样的,我们也可以很容易达到 21 天。只是,我们很容易在 21 天后失去一些目标。所以在练习开始之前,你需要创建一个帮助你提高技术的列表,然后一点点加以提高。比如说:

  1. 尝试使用 React + Redux + Koa 2、或者Angular 2 + TypeScript,这样我们就能凭此来学习新的技术。
  2. 尝试使用 CQRS 架构来设计 CMS,这样我们就可以练习在架构方面的能力。

在我们想到一点我们可以练习的技术的时候,这就是一个可以变成 Issue 管理的内容,我们就可以针对性的提高。

通常在这种情况下,我们知道自己不知道什么东西,当我们处于不知道自己不知道、不知道自己知道时,那我们就需要网上的各种技能图谱——如StuQ的技能图谱。

skilmap
skilmap

然后了解图谱上的一个个的内容,尽可能依照此构建自己的体系——以让自己走向知道自己不知道的地步,然后我们才依此来展开练习。

建议试试我们家的Growth哈,地址:http://growth.ren。

文章的剩下部分就让我分享一下:在这723天里,我创造出了哪些有意思的东西(ps:让我装逼一下)——其实我不仅仅只是 Markdown 写得好

2014年

时间:2014.10.08-2014.12.30

2014.png
2014.png

在这一段时间里,我创建的项目大部分都是一些物联网项目:

正是这几个IoT项目,让Packt出版社找到了我,才有了后来和国内外出版社打交道的故事。也开始了技术审阅、翻译、写书的各种故事,想想就觉得这个开头真的很好。

期间还创建了一个很有意思的Chrome插件,叫onebuttonapp——没错,就是模仿Amazon的一键下单写的。这个插件的目的就是难证当时在项目上用的Backbone、Require.js的这一套可以在插件上好好玩。

OnMap项目是为了让我用Nokia Lumia 920拍照的照片,可以在地图上显示而创建的项目。

当然还有其他的一些小项目啦。

2015年

2015.png
2015.png

整个区间就是刷各种前端的技术栈,创建了各种有意思的项目:

期间还创建了几个混合应用项目:

更多内容可以见我的Idea列表:https://github.com/phodal/ideas,我实在是不想写了。

2016年

2016.png
2016.png

我们有了Growth系列的电子书、APP,还有Mole,几个极具代表性的项目就够了。

See you Again

停止这次连击,只是为了有一个更好的开始。

如果你也想提高自己,不妨从创建你的 ideas 项目开始,如我的Ideas项目一样,上面已经有了大量的 Idea。然后,我们还可以依据这一个个的项目,创建出一本电子书,即 ideabook

GitHub 里程碑

写在GitHub 的第 19999 个 star 时

Star 虽好,可不要贪杯哦。 两年前在做 Annual Review 订下一年的目标时,想着写一个开源框架。去年订下今年的目标时,仍然继续着这样的想法。今年又要制定下一年的目标,2333~~。

不久前,在 GitHub Ranking 上看到自己的 star 数(star 不是设计用于做“点赞”的,而是用来收藏的)时,发现已经快 20000 了。然后把自己的项目过了一遍,发现没有一个比较好的代表性框架,要么是应用,要么是电子书。

前 8 个项目里,除了 Growth 应用以外,其他的都是电子书内容——六本电子书加起来的 star 数有 10619,果然是骗 star 的。我只能尽力地去想想:为什么事情会变成这样了?

从创建开源框架说起

创建开源框架和创建创建开源项目是不一样的,前者你服务于开发者,后者你服务于用户。

两年前在做 Annual Review 的时候,想着未来的一年里可以做一个开源框架试试。那时刚毕业不久,对开源世界的各种游戏规则不是很了解:开源并不是将代码提交上去,然后就会一下子火起来。虽然我们可以在短期内赚上一些眼球,但是真正要将它采用到项目上的人不多。

当时,我遇到的最主要的问题是:想参与到项目的人并没有遇到足够的能力。你还需要花费大量的时间去教他们,鼓励 GitHub 新手并不是一件容易的事。有时我需要在接受他的 PR 后,再修改他的代码。并且人们提交 PR 可能是出于不同的原因。

然后,知道了开源世界还有一个游戏规则是:谁的影响力大,谁就能产生更广泛的影响。如 Virtual Dom 并不是 Facebook 首创的,但是却因为 FB 火的; 松本行弘在写下 mruby 的 README 时(印象中是这个项目),star 数就已经过 1k 了。这种例子数不胜数,要么是在推广上花了力气,要么个人、公司有着更大的影响力。

一年前,稍微改变了下策略:暂时以培养人为主,同时想着做一个合适的开源框架——只是在今年看来,前端领域已经没有合适的地方可以造轮子了。

在 GitHub 上有一个很常见的问题是,大多数项目的维护者就是发起人——如果这个发起人发生意外了,那么这个项目怎么办。如果这是一个很火的项目,它就存在着巨大的风险;同时这可能也说明了,缺乏一套合理的机制。

你的开源项目不仅仅需要一个使用文档,还需要一个相关设计思想的文档、路线图、未来计划等等。

去年年底写总结的时候,想到可以 RePractise 文章为基础来培养人,于是就有了 Growth 的三个项目:

如今 Growth 已经有了过万的用户,每天活跃的用户数也接近 300 了。第一步看上去很成功,但是下一步怎么走呢?

下一个开源项目

后来我开始在思索一个问题,创建一个开源框架是必须的吗?

在编写 Growth 电子书的时候,我发现一个好的软件工程实践远远比一个易上手的框架重要多了。框架本身是易变的东西,过去你在用 Backbone,现在你在用 React.js;过去你在用 Angular.js,现在你在用 Vue。会不会使用某个框架,并不是区分你是不是一个有经验的开发者的标准。

一直将焦点关注于学习不同的框架的使用是有问题的,一个在校生可以轻松地比你了解某个框架的原理——你白天在工作,而他整天在学习。这时你很容易就失去竞争力了,你需要从框架之外了解更深层次的东西。一个好的框架并不能让你写出一段好的代码

如果中国人的思想不觉悟,即使治好了他们的病,也只是做毫无意义。

这算是我为自己在 GitHub 下的 Markdown 的自辩吧——谁让我一直有写作的冲动呢。

不过我仍然还有一些想法,只是还没有抽出足够的时间去思考这样的事。

GNU/Linux 的桌面。这是几年前的一个想法了,当时 GNU/Linux 的那些操作系统上都没有一个好玩的桌面,不过感觉这个坑太深了,就没有进行了。

家居智能中心。我仍然对于大学学的知识有点念念不忘,虽然已经写了一本书,但是硬件还是相当的刺激。唯一的问题是:连房子都没有,怎么做智能家居。

图形框架。这是我之前在做一个图形界面的时候,发现没有一个合适的框架可以满足我的要求。然后我就在想,还是自己做一个吧。

不过,最好的开源项目就是自己平时用的。于是,我开始将写各种工具来给自己使用——如现在在用的这篇微信编辑工具:mdpub

最后,我做了一个简单的 HTML 5 动画来记录这一时刻,作为这一个里程碑的记念:

https://phodal.github.io/20k/

GitHub 寻宝指南

作为一个资深的咨询师、程序员,GitHub 是我用过的最好工具,因为 Google 并非总是那么用。GitHub 是一个宝藏库,可没有藏宝图,GitHub 一1亿的仓库也和你没有关系。这么一些年下来,也算是掌握了一定的技巧,写篇文章记录一下,也就顺其自然了。

总结一句话便是:GitHub 来搜索 Google 搜索不到的。它们可以 work 的原因,都是因为我们想做的事情,已经有人已经走过。如果你走的是一条新的路,那么这篇文章对你来说,意义可能没有那么大。

寻找 Demo 节省时间

在工作上使用新的技术,和自己平时的练习,终究差得有些远。工作的时候,我们偏向于目标编程,对于速度和时间的要求,要比自己业余时间要高得多。一旦有了这种压力,便会在 GitHub 上寻找相应的 Demo,了解原理、稍微尝试,再引入到项目中。

这时,便会按技术栈的关键字搜索,并按更新时间进行排序,以查找是否有合适的 Demo。

生命有限 ,如若是每次我们尝试一个新的技术,总得自己编写一个个 Demo。编写多个 Demo,都得花去个半天八小时的时间。如此一算,能花费在其它事情上的时间便更少了。若只是试用官方的 Demo,往往是比较容易的。可我们编写应用的时候,总得结合到当前的场合来。这时整合并不是一个轻松的工作,依赖冲突、引入第三方依赖等。

温馨提醒对于简单的项目来说,自己直接写 Demo 会更加方便。尝试项目需要成本,若是需要尝试使用多个项目,那么有可能就浪费时间。

寻找脚手架:加快前期开发

无论是后端的微服务架构,还是前端应用,应用的架构正在变得复杂。后端微服务,需要结合一个个的框架,哪怕是 Spring Initializr 这样的工具,也只能帮助我们搭建项目。我们还需要配合其它工具,一起搭建出一个基本的系统。对于前端应用也是类似的,若是 Angular 这样大而全的框架,时间花费倒也是不多。如 React 这种需要组合的、小而美的框架,使用官方的 create-react-app 也很难做出我们想要的东西,寻找一个合适的脚手架是一个更好的选择。

这时,我们大抵可以,直接使用技术栈 + boilerplate 又或者是 starter 等关键词进行搜索,如 react boilerplate。如果其中找到的组合技术栈,不符合自己的要求,那么再加上相应技术栈的关键字,如 react redux boilerplate 即可。有意思的是,在这时使用 Google 会比 GitHub 方便一些。

温馨提醒:我们需要衡量:修改脚手架的成本,是否比自己重头写快

寻找 awesome-xxx:探索可能性

练习新的框架,我总习惯于,编写一系列相关的 DEMO 项目,然后使用 awesome-xxx 探索可能性。

Awesome-xxx 系列,是 GitHub 上最容易赚 star 的类型。但凡是有一定知识度的领域、语言、框架等,都有自己的 awesome-xxx 系列的项目,如 awesome-python, awesome-iot, awesome-react 等等。在这样的项目里,都以一定的知识体系整理出来的,从索引和查阅上相应的方便。如果你想进入一个新的领域,会尝试新的东西就搜索 awesome xxx 吧。

温馨提醒:awesome-xxx 只意味着它们包含尽可能多的资料,并不代表它们拥有所有相关的库。

模仿轮子的轮子

大学时,我在练习写嵌入式操作系统,uC/OS-II 对于初学者的我来说,太复杂了——有太多无关的代码。便在网上找寻相关的实现,也便是找到了一些,在那的基础上一点点完善操作系统。

学习一个成熟的框架,直接阅读现有源码的成本太高,毕竟也不经济。最好的方式,就是去造轮子。从模仿轮子之上,再去造轮子,是最省力气的方式。再配合 《造轮子与从Github生成轮子》 一文,怕是能写一系列的框架。而造一个相似轮子的想法,往往很多人都有。尤其是一个成熟的框架,往往有很多仿制品。

于是,当你想了解一个框架,造个轮子,不妨试试搜索 xxx-like 或者 xxx-like framework,中文便是 仿 react 框架 或者 类 react。如我们在 Google 上搜索 react-like 就会搜索到 inferno。不过,按 GitHub 的尿性,要搜索到这样的框架,并不是一件容易的事。这时 Google 往往比 GitHub 搜索好用。

所以建议:平时上班休息时,搜索相关的轮子,回家就可以造轮子了。

学习资源

GitHub 上拥有大量的学习资源,从各类的文章到笔记,还有各式各样的电子书。如:

  1. 只需要搜索:类型 + 笔记,如 操作系统 笔记 就能找到一些操作系统相关的笔记。
  2. 只需要搜索:书名 就能找到一些和这本书相关的资源,如 重构 改善既有代码的设计

与此同时,GitHub 上还会搜索到各种 未经授权英文书籍的翻译,又或者是各种电子书的 PDF 版。作为多本书的作译者,当然不鼓励 GitHub 上找到一些盗版书。

而在 GitHub 上又有一些库,可以提供相应的学习资源,如 free-programming-books-zh_CN,即免费的编程中文书籍索引。

建议:请尊重版权,哈哈哈。

密钥/密码

GitHub 上有太多这样的东西,尽管我没有能赶上个好时候,找到一个合适的密钥。有相关多的资料泄漏和数据库被扒,和 GitHub 上存在的密钥和密码有关。

不过,好在 GitHub 已经在着手解决这个问题:自动删除相关的提交、代码警告等等。

私有、商用的 SDK 或代码

总有人,会将一些商用的代码,或者公司内部的代码,提交到 GitHub 上。如果你偶尔看到这样的代码,除了每一时间告诉作者,还可以偷偷 Clone 一下代码——虽然这样做不对,但是我还是想看。

如在 ThoughtWorks 的面试流程里,有一个步骤是代码编程的作业,个人的实现是不能公开出来的。接到一份作业的时候,总会去 GitHub 搜索相应的代码是否被提交了。提交了,倒是也得提醒一下相应的候选人。

过去,我在使用 Phaser 编写应用的时候,对应的粒子系统是收费的。由于我只是尝试这个粒子系统,便没有购买的想法。我一想 GitHub 上可能有,于是搜索了对应的 particle-storm.js,然后就中奖了。就便愉愉快快地去写我的 Hello, World,最后发现它太耗费资源了,便放弃了。

建议:一旦你在 GitHub 上拿到别人的商用代码,请仅用于学习,并时刻保持低调。稍有不慎,有牢狱之灾。

数据及数据制作工具

当我们需要数据的时候,就会考虑写爬虫。于是 GitHub 上充满了各各样的式爬虫,除此还有得同学把爬虫数据都放在上面了。某次,当我在玩 ElasticSearch 搜索引擎的时候,突然需要一些真实的数据用来测试。便得找爬虫,就在 GitHub 上,找到了大众点评的一些爬虫。

这个关键词,就是:scrapy dianping.com,得来不费功夫。

除此,在 AI 相当流行的今天也是如此,也可以搜索到其它同学训练好的模型。

结论

试试你的 GitHub 搜索功能吧。

FAQ

如何看待github 项目刷Star行为?

我觉得:在作者开源了源码的情况下,求 star 并没有任何问题。

开源软件的源头是自由软件,而 RMS 创建自由软件的目的是,反对专利软件,即私有化的软件。如果一个开源项目,要你 star 了,才公开源码,这才叫违反。

开源一个软件,并不意味着:你不能用这个开源软件追求任何利益。在所谓的开源运动里,一个开源软件是可以用来卖钱的。可在国内,这是很难的,大公司 如腾讯,可以轻轻松松地用你的软件,而不遵循 GPL 协议。

在这种时候,也没有法律来保护这些开源软件作者。你只能从道德上谴责他们,然后指望他们的领导来做出一些什么事。如之前的《知名公司(努比亚/中兴)拿我的开源软件( XXL-JOB)申请国家知识专利,我该怎么办?》事件。

并且对于大部分的开源软件作者来说,都不大可能像 OpenResty、Vue、emqtt 等软件的作者一样,可以从开源软件获得收益来支撑他们开发。还有一些少数人,还能从开源软件中获得一些利益,提高他们今年的 KPI。然后明年的工资,又会多涨一点点。

可多数人,并没有这样的可能性。我在 GitHub 上有接近 30k 的 star(笑,有接近 20k 是属于电子书的,毕竟思想改变世界),它一点儿也不影响我涨工资。反而多了一个 GitHub “网红” 的称号,要知道在技术领域,“网红” 并不是一个好词。我观察过的大量开源爱好者,怕是比我还惨一些。明明做了很好的工作,因为宣传工作没有做好,连几个 star 都没有,后来就弃坑了。

在这个时候,求 star 就是让心里好受一些,『我做了这么多的事情,我希望得到一些认同』。如果我在一个微信群里,看了作者做了大量的提交,花费了一些心思。在这个时候,我是会去为作者点 star 的。因为我的 GitHub 上粉丝比较多,所以往往会多带来几个 star。

如果一个人在开源世界里,做了很多事情,连一个 star 都没有。那么,他/她可能就会离开开源世界。当这种事情发生多了,那么开源世界的人就变少了。任何做开源工作的人,都是值得鼓励的——不论他们是出于什么目的。


  1. https://zh.wikipedia.org/wiki/%E7%90%86%E6%9F%A5%E5%BE%B7%C2%B7%E6%96%AF%E6%89%98%E6%9B%BC

  2. https://www.gnu.org/philosophy/open-source-misses-the-point.zh-cn.html

  3. https://zh.wikipedia.org/wiki/WTFPL