diff --git a/chapters/01-prelude.md b/chapters/00-prelude.md similarity index 100% rename from chapters/01-prelude.md rename to chapters/00-prelude.md diff --git a/chapters/01-start-project.md b/chapters/01-start-project.md new file mode 100644 index 0000000..7a9d30b --- /dev/null +++ b/chapters/01-start-project.md @@ -0,0 +1,17 @@ +创建开源项目 +--- + + +取一个好的名字 +--- + + +挑选好 LICENSE +--- + + +官方主页 +--- + +GitHub Pages + diff --git a/chapters/04-create-project-documents.md b/chapters/03-create-project-documents.md similarity index 99% rename from chapters/04-create-project-documents.md rename to chapters/03-create-project-documents.md index 179bc8a..6acab04 100644 --- a/chapters/04-create-project-documents.md +++ b/chapters/03-create-project-documents.md @@ -1,4 +1,4 @@ -#创建项目文档 +# 创建项目文档 我们需要为我们的项目创建一个文档,通常我们可以将核心代码以外的东西都称为文档: diff --git a/chapters/03-build-github-project.md b/chapters/04-build-github-project.md similarity index 99% rename from chapters/03-build-github-project.md rename to chapters/04-build-github-project.md index 14cdf81..6086e2f 100644 --- a/chapters/03-build-github-project.md +++ b/chapters/04-build-github-project.md @@ -1,6 +1,6 @@ -#构建GitHub项目 +# 构建GitHub项目 -##如何用好GitHub +## 如何用好GitHub 如何用好GitHub,并实践一些敏捷软件开发是一个很有意思的事情.我们可以在上面做很多事情,从测试到CI,再到自动部署. diff --git a/chapters/05-tdd-with-autotest.md b/chapters/05-tdd-with-autotest.md index 3b0a548..0252739 100644 --- a/chapters/05-tdd-with-autotest.md +++ b/chapters/05-tdd-with-autotest.md @@ -1,4 +1,4 @@ -#测试 +# 改善 GitHub 项目代码质量:测试 ##TDD diff --git a/chapters/06-refactor-project.md b/chapters/06-refactor-project.md index 7055ca5..68a9b42 100644 --- a/chapters/06-refactor-project.md +++ b/chapters/06-refactor-project.md @@ -1,4 +1,4 @@ -#重构 +# 改善 GitHub 项目代码质量:重构 或许你应该知道了,重构是怎样的,你也知道重构能带来什么。在我刚开始学重构和设计模式的时候,我需要去找一些好的示例,以便于我更好的学习。有时候不得不创造一些更好的场景,来实现这些功能。 @@ -8,7 +8,7 @@ 艺,需要有创造性的方法。 -##为什么重构? +## 为什么重构? > 为了更好的代码。 @@ -24,7 +24,7 @@ 让我们来看看我们的第一个训练,相当有挑战性。 -##重构uMarkdown +## 重构uMarkdown 代码及setup请见github: [js-refactor](https://github.com/artisanstack/js-refactor) diff --git a/chapters/07-github-marketing.md b/chapters/07-github-marketing.md new file mode 100644 index 0000000..7a35588 --- /dev/null +++ b/chapters/07-github-marketing.md @@ -0,0 +1,132 @@ +除了擅长编写 md 电子书来攒 star,我还写了一系列的开源软件,也掌握了一些项目运营的技巧。 + +**开源并不是你把软件、README 写好就行了,还有详细的文档、示例程序等等**。 + +**开源也不是你的项目好了,就会有一堆人参与进来**。 + +**开源还要你帮助别人解决 Bug,……**。 + +**人们做事都是有原因的**,即动机。再举例一下,如果你的项目不够火,别人都没听过,那么**写到简历上可能没啥用**。 + +Marketing First +--- + +开源需要一些营销的技巧,这些技巧可以帮你吸引关注。举个简单的例子,司徒正美的 [avalon](https://github.com/RubyLouvre/avalon) 框架出身得很早,也 MV* 方面也做得很不错,但是在 marketing 上就……。以至于国内的很多前端,都不了解这个框架,要不今天在国内可能就是 AVRR 四大框架了。 + +Vue 不是因为好用,而一下子火了。这一点我印象特别深,当时在 GitHub Trending 上看到了这个项目,发现它还不能很好地 work。 + +而如文章 《[FIRST WEEK OF LAUNCHING VUE.JS](http://blog.evanyou.me/2014/02/11/first-week-of-launching-an-oss-project/)》所说,项目刚开始的时候作者做了一系列的营销计划: + + - HackerNews + - Reddit /r/javascript + - EchoJS + - The DailyJS blog + - JavaScript Weekly + - Maintain a project Twitter account(维护项目的 Vue 账户) + +除此,文中还提到了一篇文章《[How to Spread The Word About Your Code](https://hacks.mozilla.org/2013/05/how-to-spread-the-word-about-your-code/?utm_source=statuscode&utm_medium=email)》 。 + +这一点相当的有意思,如果你的想法好的话,那么大家都会肯定,点下链接,为你来个 star。那么,你就获得更好的动力去做这件事。项目也在开头的时候,获得了相当多的关注。而如果大家觉得你的项目没有新意的话,那么你懂的~。 + +除此,还有一种可能是,你的 ID 不够 fancy,即你在社区的影响上比较少。此时,就需要**一点点慢慢积累人气**了。当你积累了一些人气,你就能和松本行弘一样,在创建 mRuby 的时候就有 1000+ 的 star。并且,在社区上还有一些相关的文章介绍,各个头条也由他的粉丝发了上去。如,一年多以前,我创建了 [mole](https://github.com/phodal/mole) 项目。 + +![Mole](mole.png) + +当时,是为了给自己做一个基于 GitHub 云笔记的工具,在完成度到一定程度的时候。我在我的微信公从号上发了相关的介绍,第二天就有 100+ 的 star 了,还接收至最一些鼓舞的话语。对应于国内则有: + + - 极客头条 + - 掘金 + - 开发者头条 + - v2ex + - 知乎 + - 不成器的微博 + +所以,你觉得呢? + +编写 README +--- + +在一个开源项目里,README 是最重要的内容。它快速地介绍了这个项目,并决定了它能不能吸引用户: + + - **这个项目做什么?** + - **它解决了什么问题** + - **它有什么特性** + — **hello, world 示例** + +### 这个项目做什么——一句话文案 + +GitHub 的 Description 是我们在 Hacking News、GitHub Trneding 等等,第一时间看到的介绍。也是我们能快速介绍给别人的东西,如下图所示: + +![GitHub Trending](./img/github-trending-example.png) + +这一句话,必须简单明了也介绍,它是干什么的。 + +如 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 开源项目](./img/rpc-example.png) + +以上便是这个项目能解决的问题,不过这个项目能解决的问题倒是比较长,哈哈哈。 + +### 它有什么特性 + +当我们有 A、B、C 几个不同的框架的时候,作为一个开发人员,就需要对比他们的特性,。如下是 Go 语言实现的 MQTT 示例: + +![GO MQTT 示例](./img/go-mqtt.png) + +这个项目只支持的 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: + + - Iterating arrays, objects, & strings + - Manipulating & testing values + - Creating composite functions + +你会怎么写? + +### hello, world 示例 + +在我们看完了上面的介绍之后,紧接着就是一个 hello, world 的示例。如 React 的示例: + +``` +class HelloMessage extends React.Component { + render() { + return
Hello {this.props.name}
; + } +} + +ReactDOM.render( + , + document.getElementById('container') +); +``` + +这个 + +技术文档——手把手教会别人 +--- + + +示例程序 +--- + + + +吸引贡献者 +--- + + diff --git a/chapters/08-maintain-project.md b/chapters/08-maintain-project.md new file mode 100644 index 0000000..e69de29 diff --git a/chapters/07-find-github-project.md b/chapters/10-find-github-project.md similarity index 100% rename from chapters/07-find-github-project.md rename to chapters/10-find-github-project.md diff --git a/chapters/10-streak-your-github.md b/chapters/11-streak-your-github.md similarity index 100% rename from chapters/10-streak-your-github.md rename to chapters/11-streak-your-github.md diff --git a/chapters/11-milestone.md b/chapters/12-milestone.md similarity index 100% rename from chapters/11-milestone.md rename to chapters/12-milestone.md diff --git a/chapters/08-analytics.md b/chapters/13-analytics.md similarity index 100% rename from chapters/08-analytics.md rename to chapters/13-analytics.md diff --git a/github-roam.md b/github-roam.md index d17457e..61d8ee2 100644 --- a/github-roam.md +++ b/github-roam.md @@ -81,6 +81,24 @@ --- +创建开源项目 +--- + + +取一个好的名字 +--- + + +挑选好 LICENSE +--- + + +官方主页 +--- + +GitHub Pages + + #Git基本知识与GitHub使用 ##Git @@ -301,9 +319,82 @@ CLA即Contributor License Agreement,在为一些大的组织、机构提交Pul
-#构建GitHub项目 +# 创建项目文档 -##如何用好GitHub +我们需要为我们的项目创建一个文档,通常我们可以将核心代码以外的东西都称为文档: + +1. README +2. 文档 +3. 示例 +4. 测试 + +通常这个会在项目的最上方会有一个项目的简介,如下图所示: + +![GitHub Project Introduction](./img/github-intro.png) + +##README + +README通常会显示在GitHub项目的下面,如下图所示: + +![GitHub README](./img/readme-example.png) + +通常一个好的README会让你立马对项目产生兴趣。 + +如下面的内容是React项目的简介: + +![React README](./img/react-intro.png) + +下面的内容写清楚了他们的用途: + +* **Just the UI:** Lots of people use React as the V in MVC. Since React makes no assumptions about the rest of your technology stack, it's easy to try it out on a small feature in an existing project. +* **Virtual DOM:** React abstracts away the DOM from you, giving a simpler programming model and better performance. React can also render on the server using Node, and it can power native apps using [React Native](https://facebook.github.io/react-native/). +* **Data flow:** React implements one-way reactive data flow which reduces boilerplate and is easier to reason about than traditional data binding. + +通常在这个README里,还会有: + +* 针对人群 +* 安装指南 +* 示例 +* 运行的平台 +* 如何参与贡献 +* 协议 + +##在线文档 + +很多开源项目都会有自己的网站,并在上面有一个文档,而有的则会放在[https://readthedocs.org/](https://readthedocs.org/)。 + +> Read the Docs 托管文档,让文档可以被全文搜索和更易查找。您可以导入您使用任何常用的版本控制系统管理的文档,包括 Mercurial、Git、Subversion 和 Bazaar。 我们支持 webhooks,因此可以在您提交代码时自动构建文档。并且同样也支持版本功能,因此您可以构建来自您代码仓库中某个标签或分支的文档。查看完整的功能列表 。 + +在一个开源项目中,良好和专业的文档是相当重要的,有时他可能会比软件还会重要。因为如果一个开源项目好用的话,多数人可能不会去查看软件的代码。这就意味着,多数时候他在和你的文档打交道。文档一般会有:API 文档、 配置文档、帮助文档、用户手册、教程等等 + +写文档的软件有很多,如Markdown、Doxygen、Docbook等等。 + +##可用示例 + +一个简单上手的示例非常重要,特别是通常我们是在为着某个目的而去使用一个开源项目的是时候,我们希望能马上使用到我们的项目中。 + +你希望看到的是,你打开浏览器,输入下面的代码,然后**It Works**: + +``` +var HelloMessage = React.createClass({ + render: function() { + return
Hello {this.props.name}
; + } +}); + +React.render( + , + document.getElementById('container') +); +``` + +而不是需要繁琐的步骤才能进行下一步。 + +--- + +# 构建GitHub项目 + +## 如何用好GitHub 如何用好GitHub,并实践一些敏捷软件开发是一个很有意思的事情.我们可以在上面做很多事情,从测试到CI,再到自动部署. @@ -845,80 +936,7 @@ SQLiteHelper.prototype.getData = function (url, callback) { --- -#创建项目文档 - -我们需要为我们的项目创建一个文档,通常我们可以将核心代码以外的东西都称为文档: - -1. README -2. 文档 -3. 示例 -4. 测试 - -通常这个会在项目的最上方会有一个项目的简介,如下图所示: - -![GitHub Project Introduction](./img/github-intro.png) - -##README - -README通常会显示在GitHub项目的下面,如下图所示: - -![GitHub README](./img/readme-example.png) - -通常一个好的README会让你立马对项目产生兴趣。 - -如下面的内容是React项目的简介: - -![React README](./img/react-intro.png) - -下面的内容写清楚了他们的用途: - -* **Just the UI:** Lots of people use React as the V in MVC. Since React makes no assumptions about the rest of your technology stack, it's easy to try it out on a small feature in an existing project. -* **Virtual DOM:** React abstracts away the DOM from you, giving a simpler programming model and better performance. React can also render on the server using Node, and it can power native apps using [React Native](https://facebook.github.io/react-native/). -* **Data flow:** React implements one-way reactive data flow which reduces boilerplate and is easier to reason about than traditional data binding. - -通常在这个README里,还会有: - -* 针对人群 -* 安装指南 -* 示例 -* 运行的平台 -* 如何参与贡献 -* 协议 - -##在线文档 - -很多开源项目都会有自己的网站,并在上面有一个文档,而有的则会放在[https://readthedocs.org/](https://readthedocs.org/)。 - -> Read the Docs 托管文档,让文档可以被全文搜索和更易查找。您可以导入您使用任何常用的版本控制系统管理的文档,包括 Mercurial、Git、Subversion 和 Bazaar。 我们支持 webhooks,因此可以在您提交代码时自动构建文档。并且同样也支持版本功能,因此您可以构建来自您代码仓库中某个标签或分支的文档。查看完整的功能列表 。 - -在一个开源项目中,良好和专业的文档是相当重要的,有时他可能会比软件还会重要。因为如果一个开源项目好用的话,多数人可能不会去查看软件的代码。这就意味着,多数时候他在和你的文档打交道。文档一般会有:API 文档、 配置文档、帮助文档、用户手册、教程等等 - -写文档的软件有很多,如Markdown、Doxygen、Docbook等等。 - -##可用示例 - -一个简单上手的示例非常重要,特别是通常我们是在为着某个目的而去使用一个开源项目的是时候,我们希望能马上使用到我们的项目中。 - -你希望看到的是,你打开浏览器,输入下面的代码,然后**It Works**: - -``` -var HelloMessage = React.createClass({ - render: function() { - return
Hello {this.props.name}
; - } -}); - -React.render( - , - document.getElementById('container') -); -``` - -而不是需要繁琐的步骤才能进行下一步。 - ---- - -#测试 +# 改善 GitHub 项目代码质量:测试 ##TDD @@ -1158,7 +1176,7 @@ req.end(); --- -#重构 +# 改善 GitHub 项目代码质量:重构 或许你应该知道了,重构是怎样的,你也知道重构能带来什么。在我刚开始学重构和设计模式的时候,我需要去找一些好的示例,以便于我更好的学习。有时候不得不创造一些更好的场景,来实现这些功能。 @@ -1168,7 +1186,7 @@ req.end(); 艺,需要有创造性的方法。 -##为什么重构? +## 为什么重构? > 为了更好的代码。 @@ -1184,7 +1202,7 @@ req.end(); 让我们来看看我们的第一个训练,相当有挑战性。 -##重构uMarkdown +## 重构uMarkdown 代码及setup请见github: [js-refactor](https://github.com/artisanstack/js-refactor) @@ -1570,6 +1588,226 @@ public class replaceTemp { --- +除了擅长编写 md 电子书来攒 star,我还写了一系列的开源软件,也掌握了一些项目运营的技巧。 + +**开源并不是你把软件、README 写好就行了,还有详细的文档、示例程序等等**。 + +**开源也不是你的项目好了,就会有一堆人参与进来**。 + +**开源还要你帮助别人解决 Bug,……**。 + +**人们做事都是有原因的**,即动机。再举例一下,如果你的项目不够火,别人都没听过,那么**写到简历上可能没啥用**。 + +Marketing First +--- + +开源需要一些营销的技巧,这些技巧可以帮你吸引关注。举个简单的例子,司徒正美的 [avalon](https://github.com/RubyLouvre/avalon) 框架出身得很早,也 MV* 方面也做得很不错,但是在 marketing 上就……。以至于国内的很多前端,都不了解这个框架,要不今天在国内可能就是 AVRR 四大框架了。 + +Vue 不是因为好用,而一下子火了。这一点我印象特别深,当时在 GitHub Trending 上看到了这个项目,发现它还不能很好地 work。 + +而如文章 《[FIRST WEEK OF LAUNCHING VUE.JS](http://blog.evanyou.me/2014/02/11/first-week-of-launching-an-oss-project/)》所说,项目刚开始的时候作者做了一系列的营销计划: + + - HackerNews + - Reddit /r/javascript + - EchoJS + - The DailyJS blog + - JavaScript Weekly + - Maintain a project Twitter account(维护项目的 Vue 账户) + +除此,文中还提到了一篇文章《[How to Spread The Word About Your Code](https://hacks.mozilla.org/2013/05/how-to-spread-the-word-about-your-code/?utm_source=statuscode&utm_medium=email)》 。 + +这一点相当的有意思,如果你的想法好的话,那么大家都会肯定,点下链接,为你来个 star。那么,你就获得更好的动力去做这件事。项目也在开头的时候,获得了相当多的关注。而如果大家觉得你的项目没有新意的话,那么你懂的~。 + +除此,还有一种可能是,你的 ID 不够 fancy,即你在社区的影响上比较少。此时,就需要**一点点慢慢积累人气**了。当你积累了一些人气,你就能和松本行弘一样,在创建 mRuby 的时候就有 1000+ 的 star。并且,在社区上还有一些相关的文章介绍,各个头条也由他的粉丝发了上去。如,一年多以前,我创建了 [mole](https://github.com/phodal/mole) 项目。 + +![Mole](mole.png) + +当时,是为了给自己做一个基于 GitHub 云笔记的工具,在完成度到一定程度的时候。我在我的微信公从号上发了相关的介绍,第二天就有 100+ 的 star 了,还接收至最一些鼓舞的话语。对应于国内则有: + + - 极客头条 + - 掘金 + - 开发者头条 + - v2ex + - 知乎 + - 不成器的微博 + +所以,你觉得呢? + +编写 README +--- + +在一个开源项目里,README 是最重要的内容。它快速地介绍了这个项目,并决定了它能不能吸引用户: + + - **这个项目做什么?** + - **它解决了什么问题** + - **它有什么特性** + — **hello, world 示例** + +### 这个项目做什么——一句话文案 + +GitHub 的 Description 是我们在 Hacking News、GitHub Trneding 等等,第一时间看到的介绍。也是我们能快速介绍给别人的东西,如下图所示: + +![GitHub Trending](./img/github-trending-example.png) + +这一句话,必须简单明了也介绍,它是干什么的。 + +如 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 开源项目](./img/rpc-example.png) + +以上便是这个项目能解决的问题,不过这个项目能解决的问题倒是比较长,哈哈哈。 + +### 它有什么特性 + +当我们有 A、B、C 几个不同的框架的时候,作为一个开发人员,就需要对比他们的特性,。如下是 Go 语言实现的 MQTT 示例: + +![GO MQTT 示例](./img/go-mqtt.png) + +这个项目只支持的 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: + + - Iterating arrays, objects, & strings + - Manipulating & testing values + - Creating composite functions + +你会怎么写? + +### hello, world 示例 + +在我们看完了上面的介绍之后,紧接着就是一个 hello, world 的示例。如 React 的示例: + +``` +class HelloMessage extends React.Component { + render() { + return
Hello {this.props.name}
; + } +} + +ReactDOM.render( + , + document.getElementById('container') +); +``` + +这个 + +技术文档——手把手教会别人 +--- + + +示例程序 +--- + + + +吸引贡献者 +--- + + + +如何以“正确的姿势”阅读开源软件代码 +=== + +> 所有让你直接看最新源码的文章都是在扯淡,你应该从“某个版本”开始阅读代码。 + +我们并不建议所有的读者都直接看最新的代码,正确的姿势应该是: + +- clone某个项目的代码到本地 +- 查看这个项目的release列表 +- 找到一个看得懂的release版本,如1.0或者更早的版本 +- 读懂上一个版本的代码 +- 向后阅读大版本的源码 +- 读最新的源码 + +最好的在这个过程中,**可以自己造轮子来实现一遍**。 + +## 阅读过程 + +在我阅读的前端库、Python后台库的过程中,我们都是以造轮子为目的展开的。所以在最开始的时候,我需要一个可以工作,并且拥有我想要的功能的版本。 + +![it-works-cms.png](./img/it-works-cms.png) + +紧接着,我就可以开始去实践这个版本中的一些功能,并理解他们是怎么工作的。再用`git`大法展开之前修改的内容,可以使用IDE自带的Diff工具: + +![pycharm-diff.jpg](./img/pycharm-diff.jpg) + +或者类似于`SourceTree`这样的工具,来查看修改的内容。 + +在我们理解了基本的核心功能后,我们就可以向后查看大、中版本的更新内容了。 + +开始之前,我们希望大家对版本号管理有一些基本的认识。 +## 版本号管理 + +我最早阅读的开始软件是Linux,而下面则是Linux的Release过程: + +![linux-history.png](./img/linux-history.png) + +表格源自一本书叫《Linux内核0.11(0.95)完全注释》,简单地再介绍一下: +- 版本0.00是一个hello,world程序 +- 版本0.01包含了可以工作的代码 +- 版本0.11是基本可以正常的版本 + +这里就要扯到《GNU 风格的版本号管理策略》: + +1.项目初版本时,版本号可以为 0.1 或 0.1.0, 也可以为 1.0 或 1.0.0,如果你为人很低调,我想你会选择那个主版本号为 0 的方式; +2.当项目在进行了局部修改或 bug 修正时,主版本号和子版本号都不变,修正版本号加 1; +3. 当项目在原有的基础上增加了部分功能时,主版本号不变,子版本号加 1,修正版本号复位为 0,因而可以被忽略掉; +4.当项目在进行了重大修改或局部修正累积较多,而导致项目整体发生全局变化时,主版本号加 1; +5.另外,编译版本号一般是编译器在编译过程中自动生成的,我们只定义其格式,并不进行人为控制。 + +因此,我们可以得到几个简单的结论: +- 我们需要阅读最早的有核心代码的版本 +- 我们需要阅读1.0版本的Release +- 往后每一次大的Release我们都需要了解一下 + +## 示例 + +以Flask为例: + +一、先Clone它。 + +![clone-flask.png](./img/clone-flask.png) + +二、从Release页面找到它的早期版本: + +![flask.png](./img/flask.png) + +三、 从上面拿到它的提交号`8605cc3`,然后checkout到这次提交,查看功能。在这个版本里,一共有六百多行代码 + +![flask-0.1.png](./img/flask-0.1.png) + +还是有点长 + +四、我们可以找到它的最早版本: + +![flask-init.png](./img/flask-init.png) + +然后查看它的`flask.py`文件,只有简单的三百多行,并且还包含一系列注释: + +![flask-init.png](./img/flask-init.png) + +五、接着,再回过头去阅读 + +- 0.1版本 +- 。。。 +- 最新的0.10.1版本 + #如何在GitHub"寻找灵感(fork)" > 重造轮子是重新创造一个已有的或是已被其他人优化的基本方法。 @@ -1743,6 +1981,563 @@ Lettuce.send = function (url, method, callback, data) { --- +#GitHub连击 + +##100天 + +我也是蛮拼的,虽然我想的只是在GitHub上连击100~200天,然而到了今天也算不错。 + +![Longest Streak](./img/longest-streak.png) + +``在不停地造轮子的过程中,也不停地造车子。`` + +在那篇连续冲击365天的文章出现之前,我们公司的大大([https://github.com/dreamhead](https://github.com/dreamhead))也曾经在公司内部说过,天天commit什么的。当然这不是我的动力,在连击140天之前 + +- 给过google的``ngx_speed``、``node-coap``等项目创建过pull request +- 也有``free-programming-books``、``free-programming-books-zh_CN``这样的项目。 +- 当然还有一个连击20天。 + +对比了一下365天连击的commit,我发现我在total上整整多了近0.5倍。 + +![365 Streak](./img/365-streak.jpg) + +同时这似乎也意味着,我每天的commit数与之相比多了很多。 + +在连击20的时候,有这样的问题: *为了commit而commit代码*,最后就放弃了。 + +而现在是``为了填坑而commit``,为自己挖了太多的想法。 + +###40天的提升 + +当时我需要去印度接受毕业生培训,大概有5周左右,想着总不能空手而归。于是在国庆结束后有了第一次commit,当时旅游归来,想着自己在不同的地方有不同的照片,于是这个repo的名字是 [onmap](https://github.com/phodal/onmap)——将自己的照片显示在地图上的拍摄地点(手机是Lumia 920)。然而,中间因为修改账号的原因,丢失了commit。 + +再从印度说起,当时主要维护三个repo: + +- 物联网的CoAP协议 +- [一步步设计物联网系统](https://github.com/phodal/designiot)的电子书 +- 一个Node.js + JS的网站 + +说说最后一个,最后一个是练习的项目。因为当时培训比较无聊,业余时间比较多,英语不好,加上听不懂印度人的话。晚上基本上是在住的地方默默地写代码,所以当时的目标有这么几个: + +- TDD +- 测试覆盖率 +- 代码整洁 + +这也就是为什么那个repo有这样的一行: + +![Repo Status](./img/repo-status.png) + +做到98%的覆盖率也算蛮拼的,当然还有Code Climate也达到了4.0,也有了112个commits。因此也带来了一些提高: + +- 提高了代码的质量(code climate比jslint更注重重复代码等等一些bad smell)。 +- 对于Mock、Stub、FakesServer等用法有更好的掌握 +- 可以持续地交付软件(版本管理、自动测试、CI、部署等等) + +###100天的挑战 + +(ps:从印度回来之后,由于女朋友在泰国实习,有了更多的时间可以看书、写代码) + +有意思的是越到中间的一些时间,commits的次数上去了,除了一些简单的pull request,还有一些新的轮子出现了。 + +![Problem](./img/problem.jpg) + +这是上一星期的commits,这也就意味着,在一星期里面,我需要在8个repo里切换。而现在我又有了一个新的idea,这时就发现了一堆的问题: + + - 今天工作在这个repo上,突然发现那个repo上有issue,需要去修复,于是就放下了当前的代码。 + - 在不同的repo间切换容易分散精力 + - 很容易就发现有太多的功能可以实现,但是时间是有限的。 + - 没有足够的空闲时间,除了周末。 + - 希望去寻找那些有兴趣的人,然而却发现原来没有那么多时间去找人。 + +###140天的希冀 + +在经历了100天之后,似乎整个人都轻松了,毕竟目标是100~200天。似乎到现在,也不会有什么特殊的情怀,除了一些希冀。 + +当然,对于一个开源项目的作者来说,最好有下面的情况: + +- 很多人知道了这个项目 +- 很多人用它的项目。 +- 在某些可以用这个项目快速解决问题的地方提到了这个项目 +- 提了bug、issue、问题。 +- 提了bug,并解决了。(ps:这是最理想的情况) + + +##200天的Showcase + +今天是我连续泡在GitHub上的第200天,也是蛮高兴的,终于到达了: + +![GitHub 200 days](./img/github-200-days.png) + +故事的背影是: 去年国庆完后要去印度接受毕业生培训——就是那个神奇的国度。但是在去之前已经在项目待了九个多月,项目上的挑战越来越少,在印度的时间又算是比较多。便给自己设定了一个长期的goal,即100~200天的longest streak。 + +或许之前你看到过一篇文章[让我们连击](https://github.com/phodal/github-roam/blob/master/chapters/12-streak-your-github.md),那时已然140天,只是还是浑浑噩噩。到了今天,渐渐有了一个更清晰地思路。 + +先让我们来一下ShowCase,然后再然后,下一篇我们再继续。 + +###一些项目简述 + +上面说到的培训一开始是用Java写的一个网站,有自动测试、CI、CD等等。由于是内部组队培训,代码不能公开等等因素,加之做得无聊。顺手,拿Node.js +RESTify 做了Server,Backbone + RequireJS + jQuery 做了前台的逻辑。于是在那个日子里,也在维护一些旧的repo,如[iot-coap](https://github.com/phodal/iot-coap)、[iot](https://github.com/phodal/iot),前者是我拿到WebStorm开源License的Repo,后者则是毕业设计。 + +对于这样一个项目也需要有测试、自动化测试、CI等等。CI用的是Travics-CI。总体的技术构架如下: + +####技术栈 + +前台: + +- Backbone +- RequireJS +- Underscore +- Mustache +- Pure CSS + +后台: + +- RESTify + +测试: + +- Jasmine +- Chai +- Sinon +- Mocha +- Jasmine-jQuery + +一直写到五星期的培训结束, 只是没有自动部署。想想就觉得可以用github-page的项目多好~~。 + +过程中还有一些有意思的小项目,如: + +###google map solr polygon 搜索 + +[google map solr polygon 搜索](http://www.phodal.com/blog/google-map-width-solr-use-polygon-search/) + +![google map solr](./img/solr.png) + +代码: [https://github.com/phodal/gmap-solr](https://github.com/phodal/gmap-solr) + +###技能树 + +这个可以从两部分说起: + +####重构Skill Tree + +原来的是 + +- Knockout +- RequireJS +- jQuery +- Gulp + +![Skill Tree](./img/skilltree.jpg) + +代码: [https://github.com/phodal/skillock](https://github.com/phodal/skillock) + +####技能树Sherlock + +- D3.js +- Dagre-D3.js +- jquery.tooltipster.js +- jQuery +- Lettuce +- Knockout.js +- Require.js + +![Sherlock skill tree](./img/sherlock.png) + +代码: [https://github.com/phodal/sherlock](https://github.com/phodal/sherlock) + +####Django Ionic ElasticSearch 地图搜索 + +![Django Elastic Search](./img/elasticsearch_ionit_map.jpg) + +- ElasticSearch +- Django +- Ionic +- OpenLayers 3 + +代码: [https://github.com/phodal/django-elasticsearch](https://github.com/phodal/django-elasticsearch) + +####简历生成器 + +![Resume](./img/resume.png) + +- React +- jsPDF +- jQuery +- RequireJS +- Showdown + +代码: [https://github.com/phodal/resume](https://github.com/phodal/resume) + + +####Nginx 大数据学习 + +![Nginx Pig](./img/nginx_pig.jpg) + +- ElasticSearch +- Hadoop +- Pig + +代码: [https://github.com/phodal/learning-data/tree/master/nginx](https://github.com/phodal/learning-data/tree/master/nginx) + +####其他 + +虽然技术栈上主要集中在Python、JavaScript,当然还有一些Ruby、Pig、Shell、Java的代码,只是我还是习惯用Python和JavaScript。一些用到觉得不错的框架: + +- Ionic: 开始Hybird移动应用。 +- Django: Python Web开发利器。 +- Flask: Python Web开发小刀。 +- RequireJS: 管理js依赖。 +- Backbone: Model + View + Router。 +- Angluar: ...。 +- Knockout: MVV*。 +- React: 据说会火。 +- Cordova: Hybird应用基础。 + +还应该有: + +- ElasticSearch +- Solr +- Hadoop +- Pig +- MongoDB +- Redis + +##365天 + + 给你一年的时间,你会怎样去提高你的水平??? + +![GitHub 365](./img/github-365.jpg) + +正值这难得的sick leave(万恶的空气),码文一篇来记念一个过去的366天里。尽管想的是在今年里写一个可持续的开源框架,但是到底这依赖于一个好的idea。在我的[GitHub 孵化器](http://github.com/phodal/ideas) 页面上似乎也没有一个特别让我满意的想法,虽然上面有各种不样有意思的ideas。多数都是在过去的一年是完成的,然而有一些也是还没有做到的。 + +尽管一直在GitHub上连击看上去似乎是没有多大必要的,但是人总得有点追求。如果正是漫无目的,却又想着提高技术的同时,为什么不去试试?毕竟技术非常好、不需要太多练习的人只是少数,似乎这样的人是不存在的。大多数的人都是经过练习之后,才会达到别人口中的“技术好”。 + +这让我想起了充斥着各种气味的知乎上的一些问题,在一些智商被完虐的话题里,无一不是因为那些人学得比别人早——哪来的天才?所谓的天才,应该是未来的智能生命一般,一出生什么都知道。如果并非如此,那只是说明他练习到位了。 + +练习不到位便意味着,即使你练习的时候是一万小时的两倍,那也是无济于事的。如果你学得比别人晚,在**很长的一段时间里**(可能直到进棺材)输给别人是必然的——落后就要挨打。就好像我等毕业于一所二本垫底的学校里,如果在过去我一直保持着和别人(各种重点)一样的学习速度,那么我只能一直是Loser。 + +需要注意的是,对你来说考上二本很难,并不是因为你比别人笨。教育资源分配不均的问题,在某种程度上导致了新的阶级制度的出现。如[我的首页](https://www.phodal.com/)说的那样: **THE ONLY FAIR IS NOT FAIR**——唯一公平的是它是不公平的。我们可以做的还有很多——**CREATE & SHARE**。真正的不幸是,因为营养不良导致的教育问题。 + +于是在想明白了很多事的时候起,便有了Re-Practise这样的计划,而365天只是中间的一个产物。 + +###编程的基础能力 + +虽说算法很重要,但是编码才是基础能力。算法与编程在某种程度上是不同的领域,算法编程是在编程上面的一级。算法写得再好,如果别人很难直接拿来复用,在别人眼里就是shit。想出能work的代码一件简单的事,学会对其重构,使之变得更易读就是一件有意义的事。 + +于是,在某一时刻在GitHub上创建了一个组织,叫[Artisan Stack](https://github.com/artisanstack)。当时想的是在GitHub寻找一些JavaScript项目,对其代码进行重构。但是到底是影响力不够哈,参与的人数比较少。 + +####重构 + +如果你懂得如何写出高可读的代码,那么我想你是不需要这个的,但是这意味着你花了更多的时候在思考上了。当谈论重构的时候,让我想起了TDD(测试驱动开发)。即使不是TDD,那么如果你写着测试,那也是可以重构的。(之前写过一些利用Intellij IDEA重构的文章:[提炼函数](https://www.phodal.com/blog/intellij-idea-refactor-extract-method/)、[以查询取代临时变量](https://www.phodal.com/blog/intellij-idea-refactor-replace-temp-with-query/)、[重构与Intellij Idea初探](https://www.phodal.com/blog/thoughtworks-refactor-and-intellij-idea/)、[内联函数](https://www.phodal.com/blog/intellij-idea-refactor-inline-method/)) + +在各种各样的文章里,我们看到过一些相关的内容,最好的参考莫过于《重构》一书。最基础不过的原则便是函数名,取名字很难,取别人能读懂的名字更难。其他的便有诸如长函数、过大的类、重复代码等等。在我有限的面试别人的经历里,这些问题都是最常见的。 + +####测试 + +而如果没有测试,其他都是扯淡。写好测试很难,写个测试算是一件容易的事。只是有些容易我们会为了测试而测试。 + +在我写[EchoesWorks](https://github.com/echoesworks/echoesworks)和[Lan](https://github.com/phodal/lan)的过程中,我尽量去保证足够高的测试覆盖率。 + +![lan](./img/lan.png) + +![EchoesWorks](./img/echoesworks.png) + +从测试开始的TDD,会保证方法是可测的。从功能到测试则可以提供工作次效率,但是只会让测试成为测试,而不是代码的一部分。 + +测试是代码的最后一公里。所以,尽可能的为你的GitHub上的项目添加测试。 + +####编码的过程 + +初到TW时,Pair时候总会有人教我如何开始编码,这应该也是一项基础的能力。结合日常,重新演绎一下这个过程: + +1. 有一个可衡量、可实现、过程可测的目标 +2. Tasking (即对要实现的目标过程进行分解) +3. 一步步实现 (如TDD) +4. 实现目标 + +放到当前的场景就是: + +1. 我想在GitHub上连击365天。对应于每一个时候段的目标都应该是可以衡量、测试的——即每天都会有Contributions。 +2. 分解就是一个痛苦的过程。理想情况下,我们应该会有每天提交,但是这取决于你的repo的数量,如果没有新的idea出现,那么这个就变成为了Contributions而Commit。 +3. 一步步实现 + +在我们实际工作中也是如此,接到一个任务,然后分解,一步步完成。不过实现会稍微复杂一些,因为事务总会有抢占和优先级的。 + +###技术与框架设计 + +在上上一篇博客中《[After 500: 写了第500篇博客,然后呢?](https://www.phodal.com/blog/after-500-blogposts-analytics-after-tech/)》也深刻地讨论了下这个问题,技术向来都是后发者优势。对于技术人员来说,也是如此,后发者占据很大的优势。 + +如果我们只是单纯地把我们的关注点仅仅放置于技术上,那么我们就不具有任何的优势。而依赖于我们的编程经验,我们可以在特定的时候创造一些框架。而架构的设计本身就是一件有意思的事,大抵是因为程序员都喜欢创造。(ps:之前曾经写过这样一篇文章,《[对不起,我并不热爱编程,我只喜欢创造](https://www.phodal.com/blog/sorry-i-don't-like-programming/)》) + +**创造是一种知识的再掌握过程。** + +回顾一下写echoesworks的过程,一开始我需要的是一个网页版的PPT,当然这类的东西已经有很多了,如impress.js、bespoke.js等等。分析一下所需要的功能:markdown解析器、键盘事件处理、Ajax、进度条显示、图片处理、Slide。我们可以在GitHub上找到各式各样的模块,我们所要做的就是将之结合在一样。在那之前,我试着用类似的原理写(组合)了[Lettuce](https://github.com/phodal/lettuce)。 + +组合相比于创造过程是一个更有挑战性的过程,我们需要在这过程去设计胶水来粘合这些代码,并在最终可以让他工作。这好比是我们在平时接触到的任务划分,每个人负责相应的模块,最后整合。 + +我在写[lan](https://github.com/phodal/lan)的时候,也是类似的,但是不同的是我已经设计了一个清晰的架构图。 + +![Lan IoT](./img/lan-iot.jpg) + +而在我们实现的编码过程也是如此,使用不同的框架,并且让他们能工作。如早期玩的[moqi.mobi](https://github.com/echoesworks/moqi.mobi),基于Backbone、RequireJS、Underscore、Mustache、Pure CSS。在随后的时间里,用React替换了View层,就有了[backbone-react](https://github.com/phodal/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](./img/github-500.jpg) + +虽然不是连击次数最多的,但是根据[Most active GitHub users ](http://git.io/top)的结果来说,好似是大陆提交数最多的人,没有之一。再考虑到提交都是有意义的——不是机器刷出来的,不是有意识的去刷,我觉得还是有很大成就感的。 + +而要实现500天连击很重要的两点是:时间和idea。但是我觉得idea并不是非常重要的,我们可以造轮子,这一点就是在早期我做得最多的一件事,不断地造轮子——如《[造轮子与从Github生成轮子](https://www.phodal.com/blog/create-framework-from-github/)》一文中所说。除此,你还可以用《[GitHub去管理你的idea](https://www.phodal.com/blog/use-github-manage-idea/)》,每当你想到一个Idea以及完成一个idea的时间你就会多一次提交。 + +时间则是一件很讽刺的事,因为人们要加班。加班的原因,要么是因为工作的内容很有意思,要么是因为钱。如果不是因为钱的话,为什么不去换个工作呢?比如我司。看似两者间存在很多的对立,但是我总在想技术的提升可以在后期解决收入的问题,而不需要靠加班来解决这个问题。人总是要活着的,钱是必需的,但是程序员的收入都不低。 + +###编程的情绪周期 + +接着,我观察到了一些有意思的现象——编程的情绪周期也很明显。 + +> 所谓“情绪周期”,是指一个人的情绪高潮和低潮的交替过程所经历的时间。 + +如下图所示的就是情绪周期: + +![情绪周期](./img/qingxu.jpg) + +简单地来说,就是**有一个时间段写代码的感觉超级爽,有一个时间段不想写代码**,但是如果换一个说法就是:**有一个时间段看书、写文档的感觉很爽,有一时间段不想看书、写文档的感觉**。这也就是为什么在我的GitHub首页上的绿色各种花。不过因为《物联网周报》的原因,我会定期地更新一个相关的开源项目。 + +但是总来说,我习惯在一些时间造一些轮子、创建文档,这就是为什么我的GitHub会有一些开源电子书的缘故。 + +###有意图的练习 + +编程需要很长的学习时间,也需要很长的练习时间。尽管我是从小学编程,自认为天赋不错,但是突破了上个门槛还是花费了三四年的时间。其中的很大一部分原因是,没有找对一个合适的方向。而在这期间也没有好好的练习,随后的日子里我意识到我会遇到下一个门槛,便开始试图有意识的练习。 + +在我开始工作的时候,我写了一篇名为《[重新思考工作](https://www.phodal.com/blog/rethink-about-the-work/)》的文章。在文章中我提到了几点练习的点: + + - 加强码代码的准确性 + - 写出更整洁的代码 + - 英语口语 (外企) + - 针对性的加强语言技能 + +在一些日子的练习后,我发现这还是太无聊了。天生就喜欢一些有意思的东西,有趣才更有激情吧~~。不过,像下图的打字练习还是挺有意思的: + +![打字练习](./img/huovd.png) + +还是能打出了一堆错误的字符。但是对比了一下大多数人的人,还算不错,至少是盲打。但是,还是存在着很大的提升空间。 + +随后,我开始一些错误的练习,如对设计模式和架构的练习。试图去练习一些在生产上用不到的设计模式,以及一些架构模式。而这时就意味着,需要生搬一些设计模式。最后,我开始以项目为目的的练习,这就是为什么我的GitHub上的提交数会有如此多的原因。 + +###预见性练习 + +还有一种练习比较有意思,算是以工作为导向的练习。当我们预见到我们的项目需要某一些技术,我们可能在未来采用某些技术的时候,我们就需要开始预见性的练习这些技术。 + +好的一点是:这些项目可能在未来很受初学者欢迎。 + +###小结 + +每个人都有自己的方向,都有一个不错的发展路线,分享和创造都是不错的路。 + +THE ONLY FAIR IS NOT FAIR . ENJOY CREATE & SHARE. + +## 365*2-7天里 + +刚毕业的时候,有一段时间我一直困惑于如何去提高编码能力——因为项目上做的东西多数时候和自己想要的是不一样的,我便想着自己去找一些有意思的东西做着玩,在这个过程中边练习技能。 + +> 如果你知道自己代码能力不够,为什么不花两年时间去提高这方面的能力呢? + +### 编码的练习 + +编码是一件值得练习的事,你从书中、互联网上看到的那一个个的编程大牛无一不是从一点点的小技能积累起来的。从小接触可以让你有一个好的开始,一段好好的练习也会帮助你更好的前进。 + +记得我在最开始练习的时候,我分几个不同的阶段去练习: + + - 按照《重构:改善即有代码的设计》一书边寻找一些 bad smell 的代码,一边想方设法去让代码变得优雅。 + - 按照《设计模式》以及《重构与模式》来将代码重构成某种设计模式。 + - 按照《面向模式的软件架构》去设计一些软件架构。 + +而这些并不是一种容易的事,很多时候有一些模式,我们都很难有一个好的实践。只是这些东西都不是一些可以生搬硬套的,我们更需要的是知道有这些东西的存在,以便于在某一天,我们可以从我们的仓库里将这些知识取出来。 + +![10000 hours](./img/10000.png) + +我们的刻意练习加上我们的持之以恒总是会取得长足的进步。不过在我们练习之前,你需要有一个目标。这个目标可以是一个 Idea、一个设计模式、一个模仿等等,这些内容都可以以 Issue 的好好管理着。 + +在最开始我们下定目标的几天里,我们可以很容易做到这样的事。同样的,我们也可以很容易达到 21 天。只是,我们很容易在 21 天后失去一些目标。所以在练习开始之前,你需要创建一个帮助你提高技术的列表,然后一点点加以提高。比如说: + +1. 尝试使用 React + Redux + Koa 2、或者Angular 2 + TypeScript,这样我们就能凭此来学习新的技术。 +2. 尝试使用 CQRS 架构来设计 CMS,这样我们就可以练习在架构方面的能力。 + +在我们想到一点我们可以练习的技术的时候,这就是一个可以变成 Issue 管理的内容,我们就可以针对性的提高。 + +通常在这种情况下,我们知道自己不知道什么东西,当我们处于不知道自己不知道、不知道自己知道时,那我们就需要网上的各种技能图谱——如StuQ的技能图谱。 + +![skilmap](./img/skillmap.png) + +然后了解图谱上的一个个的内容,尽可能依照此构建自己的体系——以让自己走向知道自己不知道的地步,然后我们才依此来展开练习。 + +建议试试我们家的Growth哈,地址:http://growth.ren。 + +文章的剩下部分就让我分享一下:在这723天里,我创造出了哪些有意思的东西(ps:让我装逼一下)——其实我不仅仅只是 Markdown 写得好 + +#### 2014年 + +时间:2014.10.08-2014.12.30 + +![2014.png](./img/2014.png) + +在这一段时间里,我创建的项目大部分都是一些物联网项目: + + - [iot-coap](https://github.com/phodal/iot-coap) 一个基于CoAP协议的物联网 + - [designiot](https://github.com/phodal/designiot) 即电子书《教你设计物联网系统》 + - [iot-document](https://github.com/phodal/awesome-iot-document) 收集一些物联网相关的资料,和Awesome不是一个性质 + - [iot](https://github.com/phodal/iot) 基于PHP框架Laravel的物联网 + - iot-android 一个与iot项目相配套的Android程序 + - 等等 + +正是这几个IoT项目,让Packt出版社找到了我,才有了后来和国内外出版社打交道的故事。也开始了技术审阅、翻译、写书的各种故事,想想就觉得这个开头真的很好。 + +期间还创建了一个很有意思的Chrome插件,叫onebuttonapp——没错,就是模仿Amazon的一键下单写的。这个插件的目的就是难证当时在项目上用的Backbone、Require.js的这一套可以在插件上好好玩。 + +OnMap项目是为了让我用Nokia Lumia 920拍照的照片,可以在地图上显示而创建的项目。 + +当然还有其他的一些小项目啦。 + +#### 2015年 + +![2015.png](./img/2015.png) + +整个区间就是刷各种前端的技术栈,创建了各种有意思的项目: + + - [Lettuce框架](https://github.com/phodal/lettuce),一个基于简单的SPA框架 + - [echoesworks](https://github.com/phodal/echoesworks),一个支持字幕、Markdown、动画的Slide框架 + - [diaonan](https://github.com/phodal/diaonan),一个支持CoAP、MQTT、HTTP的物联网项目 + - [developer](https://github.com/phodal/developer),收集各种 Web Developer 成长路线,以及读书图谱 + + +期间还创建了几个混合应用项目: + + - [learning-ionic](https://github.com/phodal/learning-ionic),程序语言答人,各种hello,world的小应用 + - [ionic-elasticsearch](https://github.com/phodal/ionic-elasticsearch), Django ElasticSearch Ionic 打造 GIS 移动应用 + - [designiot-app](https://github.com/phodal/designiot-app),教你设计物联网APP版 + +更多内容可以见我的Idea列表:[https://github.com/phodal/ideas](https://github.com/phodal/ideas),我实在是不想写了。 + +#### 2016年 + +![2016.png](./img/2016.png) + +我们有了Growth系列的电子书、APP,还有Mole,几个极具代表性的项目就够了。 + + - [Growth](https://github.com/phodal/growth),一款专注于Web开发者成长的应用,涵盖Web开发的流程及技术栈,Web开发的学习路线、成长衡量等各方面。 + - [Growth: 全栈增长工程师指南](https://github.com/phodal/growth-ebook),一本关于如何成为全栈增长工程师的指南 + - [Growth: 全栈增长工程师实战](https://github.com/phodal/growth-in-action),在Growth中我们介绍的只是一系列的实践,而Growth实战则会带领读者去履行这些实践 + +### See you Again + +停止这次连击,只是为了有一个更好的开始。 + +如果你也想提高自己,不妨从创建你的 ideas 项目开始,如我的[Ideas](https://github.com/phodal/ideas)项目一样,上面已经有了大量的 Idea。然后,我们还可以依据这一个个的项目,创建出一本电子书,即 [ideabook](https://github.com/phodal/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](https://github.com/phodal/growth) + - 电子书:《[Growth:全栈增长工程师指南](https://github.com/phodal/growth-ebook)》 + - 电子书:《[Growth:全栈增长工程师实战](https://github.com/phodal/growth-in-action)》 + +如今 Growth 已经有了过万的用户,每天活跃的用户数也接近 300 了。第一步看上去很成功,但是下一步怎么走呢? + +### 下一个开源项目 + +后来我开始在思索一个问题,创建一个开源框架是必须的吗? + +在编写 Growth 电子书的时候,我发现一个好的软件工程实践远远比一个易上手的框架重要多了。框架本身是易变的东西,过去你在用 Backbone,现在你在用 React.js;过去你在用 Angular.js,现在你在用 Vue。会不会使用某个框架,并不是区分你是不是一个有经验的开发者的标准。 + +一直将焦点关注于**学习不同的框架的使用**是有问题的,一个在校生可以轻松地比你了解某个框架的原理——你白天在工作,而他整天在学习。这时你很容易就失去竞争力了,你需要从框架之外了解更深层次的东西。**一个好的框架并不能让你写出一段好的代码**。 + +> 如果中国人的思想不觉悟,即使治好了他们的病,也只是做毫无意义。 + +这算是我为自己在 GitHub 下的 Markdown 的自辩吧——谁让我一直有写作的冲动呢。 + +不过我仍然还有一些想法,只是还没有抽出足够的时间去思考这样的事。 + +**GNU/Linux 的桌面**。这是几年前的一个想法了,当时 GNU/Linux 的那些操作系统上都没有一个好玩的桌面,不过感觉这个坑太深了,就没有进行了。 + +**家居智能中心**。我仍然对于大学学的知识有点念念不忘,虽然已经写了一本书,但是硬件还是相当的刺激。唯一的问题是:连房子都没有,怎么做智能家居。 + +**图形框架**。这是我之前在做一个图形界面的时候,发生没有一个合适的框架可以满足我的要求。然后我就在想,还是自己做一个吧。 + +不过,最好的开源项目就是自己平时用的。于是,我开始将写各种工作来提自己使用——如现在在用的这篇微信编辑工具:[mdpub](https://github.com/phodal/mdpub)。 + +最后,我做了一个简单的 HTML 5 动画来记录这一时刻,作为这一个里程碑的记念: + +[https://phodal.github.io/20k/](https://phodal.github.io/20k/) + #GitHub用户分析 ##生成图表 @@ -2350,647 +3145,3 @@ def get_points(usernames): ``` 真看不出来两者有什么相似的地方 。。。。 - -如何以“正确的姿势”阅读开源软件代码 -=== - -> 所有让你直接看最新源码的文章都是在扯淡,你应该从“某个版本”开始阅读代码。 - -我们并不建议所有的读者都直接看最新的代码,正确的姿势应该是: - -- clone某个项目的代码到本地 -- 查看这个项目的release列表 -- 找到一个看得懂的release版本,如1.0或者更早的版本 -- 读懂上一个版本的代码 -- 向后阅读大版本的源码 -- 读最新的源码 - -最好的在这个过程中,**可以自己造轮子来实现一遍**。 - -## 阅读过程 - -在我阅读的前端库、Python后台库的过程中,我们都是以造轮子为目的展开的。所以在最开始的时候,我需要一个可以工作,并且拥有我想要的功能的版本。 - -![it-works-cms.png](./img/it-works-cms.png) - -紧接着,我就可以开始去实践这个版本中的一些功能,并理解他们是怎么工作的。再用`git`大法展开之前修改的内容,可以使用IDE自带的Diff工具: - -![pycharm-diff.jpg](./img/pycharm-diff.jpg) - -或者类似于`SourceTree`这样的工具,来查看修改的内容。 - -在我们理解了基本的核心功能后,我们就可以向后查看大、中版本的更新内容了。 - -开始之前,我们希望大家对版本号管理有一些基本的认识。 -## 版本号管理 - -我最早阅读的开始软件是Linux,而下面则是Linux的Release过程: - -![linux-history.png](./img/linux-history.png) - -表格源自一本书叫《Linux内核0.11(0.95)完全注释》,简单地再介绍一下: -- 版本0.00是一个hello,world程序 -- 版本0.01包含了可以工作的代码 -- 版本0.11是基本可以正常的版本 - -这里就要扯到《GNU 风格的版本号管理策略》: - -1.项目初版本时,版本号可以为 0.1 或 0.1.0, 也可以为 1.0 或 1.0.0,如果你为人很低调,我想你会选择那个主版本号为 0 的方式; -2.当项目在进行了局部修改或 bug 修正时,主版本号和子版本号都不变,修正版本号加 1; -3. 当项目在原有的基础上增加了部分功能时,主版本号不变,子版本号加 1,修正版本号复位为 0,因而可以被忽略掉; -4.当项目在进行了重大修改或局部修正累积较多,而导致项目整体发生全局变化时,主版本号加 1; -5.另外,编译版本号一般是编译器在编译过程中自动生成的,我们只定义其格式,并不进行人为控制。 - -因此,我们可以得到几个简单的结论: -- 我们需要阅读最早的有核心代码的版本 -- 我们需要阅读1.0版本的Release -- 往后每一次大的Release我们都需要了解一下 - -## 示例 - -以Flask为例: - -一、先Clone它。 - -![clone-flask.png](./img/clone-flask.png) - -二、从Release页面找到它的早期版本: - -![flask.png](./img/flask.png) - -三、 从上面拿到它的提交号`8605cc3`,然后checkout到这次提交,查看功能。在这个版本里,一共有六百多行代码 - -![flask-0.1.png](./img/flask-0.1.png) - -还是有点长 - -四、我们可以找到它的最早版本: - -![flask-init.png](./img/flask-init.png) - -然后查看它的`flask.py`文件,只有简单的三百多行,并且还包含一系列注释: - -![flask-init.png](./img/flask-init.png) - -五、接着,再回过头去阅读 - -- 0.1版本 -- 。。。 -- 最新的0.10.1版本 - -#GitHub连击 - -##100天 - -我也是蛮拼的,虽然我想的只是在GitHub上连击100~200天,然而到了今天也算不错。 - -![Longest Streak](./img/longest-streak.png) - -``在不停地造轮子的过程中,也不停地造车子。`` - -在那篇连续冲击365天的文章出现之前,我们公司的大大([https://github.com/dreamhead](https://github.com/dreamhead))也曾经在公司内部说过,天天commit什么的。当然这不是我的动力,在连击140天之前 - -- 给过google的``ngx_speed``、``node-coap``等项目创建过pull request -- 也有``free-programming-books``、``free-programming-books-zh_CN``这样的项目。 -- 当然还有一个连击20天。 - -对比了一下365天连击的commit,我发现我在total上整整多了近0.5倍。 - -![365 Streak](./img/365-streak.jpg) - -同时这似乎也意味着,我每天的commit数与之相比多了很多。 - -在连击20的时候,有这样的问题: *为了commit而commit代码*,最后就放弃了。 - -而现在是``为了填坑而commit``,为自己挖了太多的想法。 - -###40天的提升 - -当时我需要去印度接受毕业生培训,大概有5周左右,想着总不能空手而归。于是在国庆结束后有了第一次commit,当时旅游归来,想着自己在不同的地方有不同的照片,于是这个repo的名字是 [onmap](https://github.com/phodal/onmap)——将自己的照片显示在地图上的拍摄地点(手机是Lumia 920)。然而,中间因为修改账号的原因,丢失了commit。 - -再从印度说起,当时主要维护三个repo: - -- 物联网的CoAP协议 -- [一步步设计物联网系统](https://github.com/phodal/designiot)的电子书 -- 一个Node.js + JS的网站 - -说说最后一个,最后一个是练习的项目。因为当时培训比较无聊,业余时间比较多,英语不好,加上听不懂印度人的话。晚上基本上是在住的地方默默地写代码,所以当时的目标有这么几个: - -- TDD -- 测试覆盖率 -- 代码整洁 - -这也就是为什么那个repo有这样的一行: - -![Repo Status](./img/repo-status.png) - -做到98%的覆盖率也算蛮拼的,当然还有Code Climate也达到了4.0,也有了112个commits。因此也带来了一些提高: - -- 提高了代码的质量(code climate比jslint更注重重复代码等等一些bad smell)。 -- 对于Mock、Stub、FakesServer等用法有更好的掌握 -- 可以持续地交付软件(版本管理、自动测试、CI、部署等等) - -###100天的挑战 - -(ps:从印度回来之后,由于女朋友在泰国实习,有了更多的时间可以看书、写代码) - -有意思的是越到中间的一些时间,commits的次数上去了,除了一些简单的pull request,还有一些新的轮子出现了。 - -![Problem](./img/problem.jpg) - -这是上一星期的commits,这也就意味着,在一星期里面,我需要在8个repo里切换。而现在我又有了一个新的idea,这时就发现了一堆的问题: - - - 今天工作在这个repo上,突然发现那个repo上有issue,需要去修复,于是就放下了当前的代码。 - - 在不同的repo间切换容易分散精力 - - 很容易就发现有太多的功能可以实现,但是时间是有限的。 - - 没有足够的空闲时间,除了周末。 - - 希望去寻找那些有兴趣的人,然而却发现原来没有那么多时间去找人。 - -###140天的希冀 - -在经历了100天之后,似乎整个人都轻松了,毕竟目标是100~200天。似乎到现在,也不会有什么特殊的情怀,除了一些希冀。 - -当然,对于一个开源项目的作者来说,最好有下面的情况: - -- 很多人知道了这个项目 -- 很多人用它的项目。 -- 在某些可以用这个项目快速解决问题的地方提到了这个项目 -- 提了bug、issue、问题。 -- 提了bug,并解决了。(ps:这是最理想的情况) - - -##200天的Showcase - -今天是我连续泡在GitHub上的第200天,也是蛮高兴的,终于到达了: - -![GitHub 200 days](./img/github-200-days.png) - -故事的背影是: 去年国庆完后要去印度接受毕业生培训——就是那个神奇的国度。但是在去之前已经在项目待了九个多月,项目上的挑战越来越少,在印度的时间又算是比较多。便给自己设定了一个长期的goal,即100~200天的longest streak。 - -或许之前你看到过一篇文章[让我们连击](https://github.com/phodal/github-roam/blob/master/chapters/12-streak-your-github.md),那时已然140天,只是还是浑浑噩噩。到了今天,渐渐有了一个更清晰地思路。 - -先让我们来一下ShowCase,然后再然后,下一篇我们再继续。 - -###一些项目简述 - -上面说到的培训一开始是用Java写的一个网站,有自动测试、CI、CD等等。由于是内部组队培训,代码不能公开等等因素,加之做得无聊。顺手,拿Node.js +RESTify 做了Server,Backbone + RequireJS + jQuery 做了前台的逻辑。于是在那个日子里,也在维护一些旧的repo,如[iot-coap](https://github.com/phodal/iot-coap)、[iot](https://github.com/phodal/iot),前者是我拿到WebStorm开源License的Repo,后者则是毕业设计。 - -对于这样一个项目也需要有测试、自动化测试、CI等等。CI用的是Travics-CI。总体的技术构架如下: - -####技术栈 - -前台: - -- Backbone -- RequireJS -- Underscore -- Mustache -- Pure CSS - -后台: - -- RESTify - -测试: - -- Jasmine -- Chai -- Sinon -- Mocha -- Jasmine-jQuery - -一直写到五星期的培训结束, 只是没有自动部署。想想就觉得可以用github-page的项目多好~~。 - -过程中还有一些有意思的小项目,如: - -###google map solr polygon 搜索 - -[google map solr polygon 搜索](http://www.phodal.com/blog/google-map-width-solr-use-polygon-search/) - -![google map solr](./img/solr.png) - -代码: [https://github.com/phodal/gmap-solr](https://github.com/phodal/gmap-solr) - -###技能树 - -这个可以从两部分说起: - -####重构Skill Tree - -原来的是 - -- Knockout -- RequireJS -- jQuery -- Gulp - -![Skill Tree](./img/skilltree.jpg) - -代码: [https://github.com/phodal/skillock](https://github.com/phodal/skillock) - -####技能树Sherlock - -- D3.js -- Dagre-D3.js -- jquery.tooltipster.js -- jQuery -- Lettuce -- Knockout.js -- Require.js - -![Sherlock skill tree](./img/sherlock.png) - -代码: [https://github.com/phodal/sherlock](https://github.com/phodal/sherlock) - -####Django Ionic ElasticSearch 地图搜索 - -![Django Elastic Search](./img/elasticsearch_ionit_map.jpg) - -- ElasticSearch -- Django -- Ionic -- OpenLayers 3 - -代码: [https://github.com/phodal/django-elasticsearch](https://github.com/phodal/django-elasticsearch) - -####简历生成器 - -![Resume](./img/resume.png) - -- React -- jsPDF -- jQuery -- RequireJS -- Showdown - -代码: [https://github.com/phodal/resume](https://github.com/phodal/resume) - - -####Nginx 大数据学习 - -![Nginx Pig](./img/nginx_pig.jpg) - -- ElasticSearch -- Hadoop -- Pig - -代码: [https://github.com/phodal/learning-data/tree/master/nginx](https://github.com/phodal/learning-data/tree/master/nginx) - -####其他 - -虽然技术栈上主要集中在Python、JavaScript,当然还有一些Ruby、Pig、Shell、Java的代码,只是我还是习惯用Python和JavaScript。一些用到觉得不错的框架: - -- Ionic: 开始Hybird移动应用。 -- Django: Python Web开发利器。 -- Flask: Python Web开发小刀。 -- RequireJS: 管理js依赖。 -- Backbone: Model + View + Router。 -- Angluar: ...。 -- Knockout: MVV*。 -- React: 据说会火。 -- Cordova: Hybird应用基础。 - -还应该有: - -- ElasticSearch -- Solr -- Hadoop -- Pig -- MongoDB -- Redis - -##365天 - - 给你一年的时间,你会怎样去提高你的水平??? - -![GitHub 365](./img/github-365.jpg) - -正值这难得的sick leave(万恶的空气),码文一篇来记念一个过去的366天里。尽管想的是在今年里写一个可持续的开源框架,但是到底这依赖于一个好的idea。在我的[GitHub 孵化器](http://github.com/phodal/ideas) 页面上似乎也没有一个特别让我满意的想法,虽然上面有各种不样有意思的ideas。多数都是在过去的一年是完成的,然而有一些也是还没有做到的。 - -尽管一直在GitHub上连击看上去似乎是没有多大必要的,但是人总得有点追求。如果正是漫无目的,却又想着提高技术的同时,为什么不去试试?毕竟技术非常好、不需要太多练习的人只是少数,似乎这样的人是不存在的。大多数的人都是经过练习之后,才会达到别人口中的“技术好”。 - -这让我想起了充斥着各种气味的知乎上的一些问题,在一些智商被完虐的话题里,无一不是因为那些人学得比别人早——哪来的天才?所谓的天才,应该是未来的智能生命一般,一出生什么都知道。如果并非如此,那只是说明他练习到位了。 - -练习不到位便意味着,即使你练习的时候是一万小时的两倍,那也是无济于事的。如果你学得比别人晚,在**很长的一段时间里**(可能直到进棺材)输给别人是必然的——落后就要挨打。就好像我等毕业于一所二本垫底的学校里,如果在过去我一直保持着和别人(各种重点)一样的学习速度,那么我只能一直是Loser。 - -需要注意的是,对你来说考上二本很难,并不是因为你比别人笨。教育资源分配不均的问题,在某种程度上导致了新的阶级制度的出现。如[我的首页](https://www.phodal.com/)说的那样: **THE ONLY FAIR IS NOT FAIR**——唯一公平的是它是不公平的。我们可以做的还有很多——**CREATE & SHARE**。真正的不幸是,因为营养不良导致的教育问题。 - -于是在想明白了很多事的时候起,便有了Re-Practise这样的计划,而365天只是中间的一个产物。 - -###编程的基础能力 - -虽说算法很重要,但是编码才是基础能力。算法与编程在某种程度上是不同的领域,算法编程是在编程上面的一级。算法写得再好,如果别人很难直接拿来复用,在别人眼里就是shit。想出能work的代码一件简单的事,学会对其重构,使之变得更易读就是一件有意义的事。 - -于是,在某一时刻在GitHub上创建了一个组织,叫[Artisan Stack](https://github.com/artisanstack)。当时想的是在GitHub寻找一些JavaScript项目,对其代码进行重构。但是到底是影响力不够哈,参与的人数比较少。 - -####重构 - -如果你懂得如何写出高可读的代码,那么我想你是不需要这个的,但是这意味着你花了更多的时候在思考上了。当谈论重构的时候,让我想起了TDD(测试驱动开发)。即使不是TDD,那么如果你写着测试,那也是可以重构的。(之前写过一些利用Intellij IDEA重构的文章:[提炼函数](https://www.phodal.com/blog/intellij-idea-refactor-extract-method/)、[以查询取代临时变量](https://www.phodal.com/blog/intellij-idea-refactor-replace-temp-with-query/)、[重构与Intellij Idea初探](https://www.phodal.com/blog/thoughtworks-refactor-and-intellij-idea/)、[内联函数](https://www.phodal.com/blog/intellij-idea-refactor-inline-method/)) - -在各种各样的文章里,我们看到过一些相关的内容,最好的参考莫过于《重构》一书。最基础不过的原则便是函数名,取名字很难,取别人能读懂的名字更难。其他的便有诸如长函数、过大的类、重复代码等等。在我有限的面试别人的经历里,这些问题都是最常见的。 - -####测试 - -而如果没有测试,其他都是扯淡。写好测试很难,写个测试算是一件容易的事。只是有些容易我们会为了测试而测试。 - -在我写[EchoesWorks](https://github.com/echoesworks/echoesworks)和[Lan](https://github.com/phodal/lan)的过程中,我尽量去保证足够高的测试覆盖率。 - -![lan](./img/lan.png) - -![EchoesWorks](./img/echoesworks.png) - -从测试开始的TDD,会保证方法是可测的。从功能到测试则可以提供工作次效率,但是只会让测试成为测试,而不是代码的一部分。 - -测试是代码的最后一公里。所以,尽可能的为你的GitHub上的项目添加测试。 - -####编码的过程 - -初到TW时,Pair时候总会有人教我如何开始编码,这应该也是一项基础的能力。结合日常,重新演绎一下这个过程: - -1. 有一个可衡量、可实现、过程可测的目标 -2. Tasking (即对要实现的目标过程进行分解) -3. 一步步实现 (如TDD) -4. 实现目标 - -放到当前的场景就是: - -1. 我想在GitHub上连击365天。对应于每一个时候段的目标都应该是可以衡量、测试的——即每天都会有Contributions。 -2. 分解就是一个痛苦的过程。理想情况下,我们应该会有每天提交,但是这取决于你的repo的数量,如果没有新的idea出现,那么这个就变成为了Contributions而Commit。 -3. 一步步实现 - -在我们实际工作中也是如此,接到一个任务,然后分解,一步步完成。不过实现会稍微复杂一些,因为事务总会有抢占和优先级的。 - -###技术与框架设计 - -在上上一篇博客中《[After 500: 写了第500篇博客,然后呢?](https://www.phodal.com/blog/after-500-blogposts-analytics-after-tech/)》也深刻地讨论了下这个问题,技术向来都是后发者优势。对于技术人员来说,也是如此,后发者占据很大的优势。 - -如果我们只是单纯地把我们的关注点仅仅放置于技术上,那么我们就不具有任何的优势。而依赖于我们的编程经验,我们可以在特定的时候创造一些框架。而架构的设计本身就是一件有意思的事,大抵是因为程序员都喜欢创造。(ps:之前曾经写过这样一篇文章,《[对不起,我并不热爱编程,我只喜欢创造](https://www.phodal.com/blog/sorry-i-don't-like-programming/)》) - -**创造是一种知识的再掌握过程。** - -回顾一下写echoesworks的过程,一开始我需要的是一个网页版的PPT,当然这类的东西已经有很多了,如impress.js、bespoke.js等等。分析一下所需要的功能:markdown解析器、键盘事件处理、Ajax、进度条显示、图片处理、Slide。我们可以在GitHub上找到各式各样的模块,我们所要做的就是将之结合在一样。在那之前,我试着用类似的原理写(组合)了[Lettuce](https://github.com/phodal/lettuce)。 - -组合相比于创造过程是一个更有挑战性的过程,我们需要在这过程去设计胶水来粘合这些代码,并在最终可以让他工作。这好比是我们在平时接触到的任务划分,每个人负责相应的模块,最后整合。 - -我在写[lan](https://github.com/phodal/lan)的时候,也是类似的,但是不同的是我已经设计了一个清晰的架构图。 - -![Lan IoT](./img/lan-iot.jpg) - -而在我们实现的编码过程也是如此,使用不同的框架,并且让他们能工作。如早期玩的[moqi.mobi](https://github.com/echoesworks/moqi.mobi),基于Backbone、RequireJS、Underscore、Mustache、Pure CSS。在随后的时间里,用React替换了View层,就有了[backbone-react](https://github.com/phodal/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](./img/github-500.jpg) - -虽然不是连击次数最多的,但是根据[Most active GitHub users ](http://git.io/top)的结果来说,好似是大陆提交数最多的人,没有之一。再考虑到提交都是有意义的——不是机器刷出来的,不是有意识的去刷,我觉得还是有很大成就感的。 - -而要实现500天连击很重要的两点是:时间和idea。但是我觉得idea并不是非常重要的,我们可以造轮子,这一点就是在早期我做得最多的一件事,不断地造轮子——如《[造轮子与从Github生成轮子](https://www.phodal.com/blog/create-framework-from-github/)》一文中所说。除此,你还可以用《[GitHub去管理你的idea](https://www.phodal.com/blog/use-github-manage-idea/)》,每当你想到一个Idea以及完成一个idea的时间你就会多一次提交。 - -时间则是一件很讽刺的事,因为人们要加班。加班的原因,要么是因为工作的内容很有意思,要么是因为钱。如果不是因为钱的话,为什么不去换个工作呢?比如我司。看似两者间存在很多的对立,但是我总在想技术的提升可以在后期解决收入的问题,而不需要靠加班来解决这个问题。人总是要活着的,钱是必需的,但是程序员的收入都不低。 - -###编程的情绪周期 - -接着,我观察到了一些有意思的现象——编程的情绪周期也很明显。 - -> 所谓“情绪周期”,是指一个人的情绪高潮和低潮的交替过程所经历的时间。 - -如下图所示的就是情绪周期: - -![情绪周期](./img/qingxu.jpg) - -简单地来说,就是**有一个时间段写代码的感觉超级爽,有一个时间段不想写代码**,但是如果换一个说法就是:**有一个时间段看书、写文档的感觉很爽,有一时间段不想看书、写文档的感觉**。这也就是为什么在我的GitHub首页上的绿色各种花。不过因为《物联网周报》的原因,我会定期地更新一个相关的开源项目。 - -但是总来说,我习惯在一些时间造一些轮子、创建文档,这就是为什么我的GitHub会有一些开源电子书的缘故。 - -###有意图的练习 - -编程需要很长的学习时间,也需要很长的练习时间。尽管我是从小学编程,自认为天赋不错,但是突破了上个门槛还是花费了三四年的时间。其中的很大一部分原因是,没有找对一个合适的方向。而在这期间也没有好好的练习,随后的日子里我意识到我会遇到下一个门槛,便开始试图有意识的练习。 - -在我开始工作的时候,我写了一篇名为《[重新思考工作](https://www.phodal.com/blog/rethink-about-the-work/)》的文章。在文章中我提到了几点练习的点: - - - 加强码代码的准确性 - - 写出更整洁的代码 - - 英语口语 (外企) - - 针对性的加强语言技能 - -在一些日子的练习后,我发现这还是太无聊了。天生就喜欢一些有意思的东西,有趣才更有激情吧~~。不过,像下图的打字练习还是挺有意思的: - -![打字练习](./img/huovd.png) - -还是能打出了一堆错误的字符。但是对比了一下大多数人的人,还算不错,至少是盲打。但是,还是存在着很大的提升空间。 - -随后,我开始一些错误的练习,如对设计模式和架构的练习。试图去练习一些在生产上用不到的设计模式,以及一些架构模式。而这时就意味着,需要生搬一些设计模式。最后,我开始以项目为目的的练习,这就是为什么我的GitHub上的提交数会有如此多的原因。 - -###预见性练习 - -还有一种练习比较有意思,算是以工作为导向的练习。当我们预见到我们的项目需要某一些技术,我们可能在未来采用某些技术的时候,我们就需要开始预见性的练习这些技术。 - -好的一点是:这些项目可能在未来很受初学者欢迎。 - -###小结 - -每个人都有自己的方向,都有一个不错的发展路线,分享和创造都是不错的路。 - -THE ONLY FAIR IS NOT FAIR . ENJOY CREATE & SHARE. - -## 365*2-7天里 - -刚毕业的时候,有一段时间我一直困惑于如何去提高编码能力——因为项目上做的东西多数时候和自己想要的是不一样的,我便想着自己去找一些有意思的东西做着玩,在这个过程中边练习技能。 - -> 如果你知道自己代码能力不够,为什么不花两年时间去提高这方面的能力呢? - -### 编码的练习 - -编码是一件值得练习的事,你从书中、互联网上看到的那一个个的编程大牛无一不是从一点点的小技能积累起来的。从小接触可以让你有一个好的开始,一段好好的练习也会帮助你更好的前进。 - -记得我在最开始练习的时候,我分几个不同的阶段去练习: - - - 按照《重构:改善即有代码的设计》一书边寻找一些 bad smell 的代码,一边想方设法去让代码变得优雅。 - - 按照《设计模式》以及《重构与模式》来将代码重构成某种设计模式。 - - 按照《面向模式的软件架构》去设计一些软件架构。 - -而这些并不是一种容易的事,很多时候有一些模式,我们都很难有一个好的实践。只是这些东西都不是一些可以生搬硬套的,我们更需要的是知道有这些东西的存在,以便于在某一天,我们可以从我们的仓库里将这些知识取出来。 - -![10000 hours](./img/10000.png) - -我们的刻意练习加上我们的持之以恒总是会取得长足的进步。不过在我们练习之前,你需要有一个目标。这个目标可以是一个 Idea、一个设计模式、一个模仿等等,这些内容都可以以 Issue 的好好管理着。 - -在最开始我们下定目标的几天里,我们可以很容易做到这样的事。同样的,我们也可以很容易达到 21 天。只是,我们很容易在 21 天后失去一些目标。所以在练习开始之前,你需要创建一个帮助你提高技术的列表,然后一点点加以提高。比如说: - -1. 尝试使用 React + Redux + Koa 2、或者Angular 2 + TypeScript,这样我们就能凭此来学习新的技术。 -2. 尝试使用 CQRS 架构来设计 CMS,这样我们就可以练习在架构方面的能力。 - -在我们想到一点我们可以练习的技术的时候,这就是一个可以变成 Issue 管理的内容,我们就可以针对性的提高。 - -通常在这种情况下,我们知道自己不知道什么东西,当我们处于不知道自己不知道、不知道自己知道时,那我们就需要网上的各种技能图谱——如StuQ的技能图谱。 - -![skilmap](./img/skillmap.png) - -然后了解图谱上的一个个的内容,尽可能依照此构建自己的体系——以让自己走向知道自己不知道的地步,然后我们才依此来展开练习。 - -建议试试我们家的Growth哈,地址:http://growth.ren。 - -文章的剩下部分就让我分享一下:在这723天里,我创造出了哪些有意思的东西(ps:让我装逼一下)——其实我不仅仅只是 Markdown 写得好 - -#### 2014年 - -时间:2014.10.08-2014.12.30 - -![2014.png](./img/2014.png) - -在这一段时间里,我创建的项目大部分都是一些物联网项目: - - - [iot-coap](https://github.com/phodal/iot-coap) 一个基于CoAP协议的物联网 - - [designiot](https://github.com/phodal/designiot) 即电子书《教你设计物联网系统》 - - [iot-document](https://github.com/phodal/awesome-iot-document) 收集一些物联网相关的资料,和Awesome不是一个性质 - - [iot](https://github.com/phodal/iot) 基于PHP框架Laravel的物联网 - - iot-android 一个与iot项目相配套的Android程序 - - 等等 - -正是这几个IoT项目,让Packt出版社找到了我,才有了后来和国内外出版社打交道的故事。也开始了技术审阅、翻译、写书的各种故事,想想就觉得这个开头真的很好。 - -期间还创建了一个很有意思的Chrome插件,叫onebuttonapp——没错,就是模仿Amazon的一键下单写的。这个插件的目的就是难证当时在项目上用的Backbone、Require.js的这一套可以在插件上好好玩。 - -OnMap项目是为了让我用Nokia Lumia 920拍照的照片,可以在地图上显示而创建的项目。 - -当然还有其他的一些小项目啦。 - -#### 2015年 - -![2015.png](./img/2015.png) - -整个区间就是刷各种前端的技术栈,创建了各种有意思的项目: - - - [Lettuce框架](https://github.com/phodal/lettuce),一个基于简单的SPA框架 - - [echoesworks](https://github.com/phodal/echoesworks),一个支持字幕、Markdown、动画的Slide框架 - - [diaonan](https://github.com/phodal/diaonan),一个支持CoAP、MQTT、HTTP的物联网项目 - - [developer](https://github.com/phodal/developer),收集各种 Web Developer 成长路线,以及读书图谱 - - -期间还创建了几个混合应用项目: - - - [learning-ionic](https://github.com/phodal/learning-ionic),程序语言答人,各种hello,world的小应用 - - [ionic-elasticsearch](https://github.com/phodal/ionic-elasticsearch), Django ElasticSearch Ionic 打造 GIS 移动应用 - - [designiot-app](https://github.com/phodal/designiot-app),教你设计物联网APP版 - -更多内容可以见我的Idea列表:[https://github.com/phodal/ideas](https://github.com/phodal/ideas),我实在是不想写了。 - -#### 2016年 - -![2016.png](./img/2016.png) - -我们有了Growth系列的电子书、APP,还有Mole,几个极具代表性的项目就够了。 - - - [Growth](https://github.com/phodal/growth),一款专注于Web开发者成长的应用,涵盖Web开发的流程及技术栈,Web开发的学习路线、成长衡量等各方面。 - - [Growth: 全栈增长工程师指南](https://github.com/phodal/growth-ebook),一本关于如何成为全栈增长工程师的指南 - - [Growth: 全栈增长工程师实战](https://github.com/phodal/growth-in-action),在Growth中我们介绍的只是一系列的实践,而Growth实战则会带领读者去履行这些实践 - -### See you Again - -停止这次连击,只是为了有一个更好的开始。 - -如果你也想提高自己,不妨从创建你的 ideas 项目开始,如我的[Ideas](https://github.com/phodal/ideas)项目一样,上面已经有了大量的 Idea。然后,我们还可以依据这一个个的项目,创建出一本电子书,即 [ideabook](https://github.com/phodal/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](https://github.com/phodal/growth) - - 电子书:《[Growth:全栈增长工程师指南](https://github.com/phodal/growth-ebook)》 - - 电子书:《[Growth:全栈增长工程师实战](https://github.com/phodal/growth-in-action)》 - -如今 Growth 已经有了过万的用户,每天活跃的用户数也接近 300 了。第一步看上去很成功,但是下一步怎么走呢? - -### 下一个开源项目 - -后来我开始在思索一个问题,创建一个开源框架是必须的吗? - -在编写 Growth 电子书的时候,我发现一个好的软件工程实践远远比一个易上手的框架重要多了。框架本身是易变的东西,过去你在用 Backbone,现在你在用 React.js;过去你在用 Angular.js,现在你在用 Vue。会不会使用某个框架,并不是区分你是不是一个有经验的开发者的标准。 - -一直将焦点关注于**学习不同的框架的使用**是有问题的,一个在校生可以轻松地比你了解某个框架的原理——你白天在工作,而他整天在学习。这时你很容易就失去竞争力了,你需要从框架之外了解更深层次的东西。**一个好的框架并不能让你写出一段好的代码**。 - -> 如果中国人的思想不觉悟,即使治好了他们的病,也只是做毫无意义。 - -这算是我为自己在 GitHub 下的 Markdown 的自辩吧——谁让我一直有写作的冲动呢。 - -不过我仍然还有一些想法,只是还没有抽出足够的时间去思考这样的事。 - -**GNU/Linux 的桌面**。这是几年前的一个想法了,当时 GNU/Linux 的那些操作系统上都没有一个好玩的桌面,不过感觉这个坑太深了,就没有进行了。 - -**家居智能中心**。我仍然对于大学学的知识有点念念不忘,虽然已经写了一本书,但是硬件还是相当的刺激。唯一的问题是:连房子都没有,怎么做智能家居。 - -**图形框架**。这是我之前在做一个图形界面的时候,发生没有一个合适的框架可以满足我的要求。然后我就在想,还是自己做一个吧。 - -不过,最好的开源项目就是自己平时用的。于是,我开始将写各种工作来提自己使用——如现在在用的这篇微信编辑工具:[mdpub](https://github.com/phodal/mdpub)。 - -最后,我做了一个简单的 HTML 5 动画来记录这一时刻,作为这一个里程碑的记念: - -[https://phodal.github.io/20k/](https://phodal.github.io/20k/) diff --git a/img/github-trending-example.png b/img/github-trending-example.png new file mode 100644 index 0000000..f6cfed0 Binary files /dev/null and b/img/github-trending-example.png differ diff --git a/img/go-mqtt.png b/img/go-mqtt.png new file mode 100644 index 0000000..502feac Binary files /dev/null and b/img/go-mqtt.png differ diff --git a/img/growth-ebook-example.png b/img/growth-ebook-example.png new file mode 100644 index 0000000..730bcb3 Binary files /dev/null and b/img/growth-ebook-example.png differ diff --git a/img/growth.png b/img/growth.png new file mode 100644 index 0000000..2d1b295 Binary files /dev/null and b/img/growth.png differ diff --git a/img/grwoth-old.png b/img/grwoth-old.png new file mode 100644 index 0000000..708a6f1 Binary files /dev/null and b/img/grwoth-old.png differ diff --git a/img/lettuce.png b/img/lettuce.png new file mode 100644 index 0000000..d11ce07 Binary files /dev/null and b/img/lettuce.png differ diff --git a/img/mole.png b/img/mole.png new file mode 100644 index 0000000..2891411 Binary files /dev/null and b/img/mole.png differ diff --git a/img/react-features-example.png b/img/react-features-example.png new file mode 100644 index 0000000..cb12de4 Binary files /dev/null and b/img/react-features-example.png differ diff --git a/img/rpc-example.png b/img/rpc-example.png new file mode 100644 index 0000000..f507a56 Binary files /dev/null and b/img/rpc-example.png differ diff --git a/index.html b/index.html index 2652c6f..c1a5959 100644 --- a/index.html +++ b/index.html @@ -75,6 +75,10 @@ code > span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Inf
  • 获得一份工作
  • 扩大交际
  • +
  • 创建开源项目
  • +
  • 取一个好的名字
  • +
  • 挑选好 LICENSE
  • +
  • 官方主页
  • Git基本知识与GitHub使用
    • Git
        @@ -91,6 +95,11 @@ code > span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Inf
      • CLA
  • +
  • 创建项目文档
  • 构建GitHub项目
  • -
  • 创建项目文档
  • -
  • 测试
      +
    • 改善 GitHub 项目代码质量:测试
    • -
    • 重构

      前言

      @@ -261,6 +275,11 @@ code > span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Inf

      扩大交际

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


      +

      创建开源项目

      +

      取一个好的名字

      +

      挑选好 LICENSE

      +

      官方主页

      +

      GitHub Pages

      Git基本知识与GitHub使用

      Git

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

      @@ -511,6 +530,65 @@ git push -u origin master

      他们都要求我签署CLA。


      +

      创建项目文档

      +

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

      +
        +
      1. README
      2. +
      3. 文档
      4. +
      5. 示例
      6. +
      7. 测试
      8. +
      +

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

      +
      +GitHub Project Introduction
      GitHub Project Introduction
      +
      +

      README

      +

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

      +
      +GitHub README
      GitHub README
      +
      +

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

      +

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

      +
      +React README
      React README
      +
      +

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

      +
        +
      • Just the UI: Lots of people use React as the V in MVC. Since React makes no assumptions about the rest of your technology stack, it’s easy to try it out on a small feature in an existing project.
      • +
      • Virtual DOM: React abstracts away the DOM from you, giving a simpler programming model and better performance. React can also render on the server using Node, and it can power native apps using React Native.
      • +
      • Data flow: React implements one-way reactive data flow which reduces boilerplate and is easier to reason about than traditional data binding.
      • +
      +

      通常在这个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

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

      @@ -1050,66 +1128,7 @@ line 21 col 62 Strings must use singlequote. };

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


      -

      创建项目文档

      -

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

      -
        -
      1. README
      2. -
      3. 文档
      4. -
      5. 示例
      6. -
      7. 测试
      8. -
      -

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

      -
      -GitHub Project Introduction
      GitHub Project Introduction
      -
      -

      README

      -

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

      -
      -GitHub README
      GitHub README
      -
      -

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

      -

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

      -
      -React README
      React README
      -
      -

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

      -
        -
      • Just the UI: Lots of people use React as the V in MVC. Since React makes no assumptions about the rest of your technology stack, it’s easy to try it out on a small feature in an existing project.
      • -
      • Virtual DOM: React abstracts away the DOM from you, giving a simpler programming model and better performance. React can also render on the server using Node, and it can power native apps using React Native.
      • -
      • Data flow: React implements one-way reactive data flow which reduces boilerplate and is easier to reason about than traditional data binding.
      • -
      -

      通常在这个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 项目代码质量:测试

      TDD

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

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

      @@ -1123,9 +1142,9 @@ React.render( return res.end('Unauthorized'); }

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

      -
      mosquitto_pub -u root -P root -h localhost -d -t lettuce -m "Hello, MQTT. This is my first message."
      +
      mosquitto_pub -u root -P root -h localhost -d -t lettuce -m "Hello, MQTT. This is my first message."

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

      -
      coap-client -m get coap://127.0.0.1:5683/topics/zero -T
      +
      coap-client -m get coap://127.0.0.1:5683/topics/zero -T

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

      var coap     = require('coap');
       var request  = coap.request;
      @@ -1278,7 +1297,7 @@ beforeEach(function() {
           });
       });

      -

      重构

      +

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

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

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

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

      @@ -1381,16 +1400,16 @@ str = tableHandler(str编写测试->功能代码->修改测试->重构

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

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

      -
      package com.phodal.learing;
      +
      package com.phodal.learing;
       
       public class Main {
       
      -    public static void main(String[] args) {
      +    public static void main(String[] args) {
               int c=new Cal().add(1,2);
               int d=new Cal2().sub(2,1);
      -        System.out.println("Hello,s");
      -        System.out.println(c);
      -        System.out.println(d);
      +        System.out.println("Hello,s");
      +        System.out.println(c);
      +        System.out.println(d);
           }
       }

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

      @@ -1402,16 +1421,16 @@ str = tableHandler(str把光标移到int d中的d,按下shift+f6,输入result_sub

    于是就有

    -
    package com.phodal.learing;
    +
    package com.phodal.learing;
     
     public class Main {
     
    -    public static void main(String[] args) {
    +    public static void main(String[] args) {
             int result_add=new Cal().add(1,2);
             int result_sub=new Cal2().sub(2,1);
    -        System.out.println("Hello,s");
    -        System.out.println(result_add);
    -        System.out.println(result_sub);
    +        System.out.println("Hello,s");
    +        System.out.println(result_add);
    +        System.out.println(result_sub);
         }
     }

    Extract Method

    @@ -1423,16 +1442,16 @@ str = tableHandler(str在弹出的窗口中输入mprint
  • 于是有了

    -
    public static void main(String[] args) {
    +
    public static void main(String[] args) {
         int result_add=new Cal().add(1,2);
         int result_sub=new Cal2().sub(2,1);
    -    System.out.println("Hello,s");
    +    System.out.println("Hello,s");
         mprint(result_add);
         mprint(result_sub);
     }
     
     private static void mprint(int result_sub) {
    -    System.out.println(result_sub);
    +    System.out.println(result_sub);
     }

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

    Inline Method

    @@ -1444,12 +1463,12 @@ str = tableHandler(str选中Inline all invocations and remove the method(2 occurrences) 点确定

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

    -
    public static void main(String[] args) {
    +
    public static void main(String[] args) {
         int result_add=new Cal().add(1,2);
         int result_sub=new Cal2().sub(2,1);
    -    System.out.println("Hello,s");
    -    System.out.println(result_add);
    -    System.out.println(result_sub);
    +    System.out.println("Hello,s");
    +    System.out.println(result_add);
    +    System.out.println(result_sub);
     }

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

    Pull Members Up

    @@ -1489,7 +1508,7 @@ str = tableHandler(str重构之前

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

    以书中的代码为例

    -
    import java.lang.System;
    +
    import java.lang.System;
     
     public class replaceTemp {
         public void count() {
    @@ -1501,13 +1520,13 @@ str = tableHandler(str
    -

    重构

    +

    重构

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

    Replace Temp With Query
    Replace Temp With Query

    便会返回

    -
    import java.lang.System;
    +
    import java.lang.System;
     
     public class replaceTemp {
         public void count() {
    @@ -1534,9 +1553,9 @@ str = tableHandler(str
    public class replaceTemp {
     
         public void method() {
    -        String str = "str";
    -        String aString = returnString().concat(str);
    -        System.out.println(aString);
    +        String str = "str";
    +        String aString = returnString().concat(str);
    +        System.out.println(aString);
         }
     
     }
    @@ -1558,6 +1577,160 @@ public class replaceTemp { }

    +

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

    +

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

    +

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

    +

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

    +

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

    +

    Marketing First

    +

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

    +

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

    +

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

    +
      +
    • HackerNews
    • +
    • Reddit /r/javascript
    • +
    • EchoJS
    • +
    • The DailyJS blog
    • +
    • JavaScript Weekly
    • +
    • Maintain a project Twitter account(维护项目的 Vue 账户)
    • +
    +

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

    +

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

    +

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

    +
    +Mole
    Mole
    +
    +

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

    +
      +
    • 极客头条
    • +
    • 掘金
    • +
    • 开发者头条
    • +
    • v2ex
    • +
    • 知乎
    • +
    • 不成器的微博
    • +
    +

    所以,你觉得呢?

    +

    编写 README

    +

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

    +
      +
    • 这个项目做什么?
    • +
    • 它解决了什么问题
    • +
    • 它有什么特性hello, world 示例
    • +
    +

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

    +

    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:

    +
    +
      +
    • Iterating arrays, objects, & strings
    • +
    • Manipulating & testing values
    • +
    • Creating composite functions
    • +
    +

    你会怎么写?

    +

    hello, world 示例

    +

    在我们看完了上面的介绍之后,紧接着就是一个 hello, world 的示例。如 React 的示例:

    +
    class HelloMessage extends React.Component {
    +  render() {
    +    return <div>Hello {this.props.name}</div>;
    +  }
    +}
    +
    +ReactDOM.render(
    +  <HelloMessage name="John" />,
    +  document.getElementById('container')
    +);
    +

    这个

    +

    技术文档——手把手教会别人

    +

    示例程序

    +

    吸引贡献者

    +

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

    +
    +

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

    +
    +

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

    +
      +
    • clone某个项目的代码到本地
    • +
    • 查看这个项目的release列表
    • +
    • 找到一个看得懂的release版本,如1.0或者更早的版本
    • +
    • 读懂上一个版本的代码
    • +
    • 向后阅读大版本的源码
    • +
    • 读最新的源码
    • +
    +

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

    +

    阅读过程

    +

    在我阅读的前端库、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)完全注释》,简单地再介绍一下: - 版本0.00是一个hello,world程序 - 版本0.01包含了可以工作的代码 - 版本0.11是基本可以正常的版本

    +

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

    +

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

    +

    因此,我们可以得到几个简单的结论: - 我们需要阅读最早的有核心代码的版本 - 我们需要阅读1.0版本的Release - 往后每一次大的Release我们都需要了解一下

    +

    示例

    +

    以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
    +
    +

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

    +
      +
    • 0.1版本
    • +
    • 。。。
    • +
    • 最新的0.10.1版本
    • +

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

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

    @@ -1706,506 +1879,6 @@ public class replaceTemp { request.send(data); };

    -

    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都有。

    -

    数据解析

    -
    import json
    -for line in open(jsonfile):
    -    line = f.readline()
    -

    然后再解析json

    -
    import dateutil.parser
    -
    -lin = json.loads(line)
    -date = dateutil.parser.parse(lin["created_at"])
    -

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

    -
    def parse_data(jsonfile):
    -    f = open(jsonfile, "r")
    -    dataarray = []
    -    datacount = 0
    -
    -    for line in open(jsonfile):
    -        line = f.readline()
    -        lin = json.loads(line)
    -        date = dateutil.parser.parse(lin["created_at"])
    -        datacount += 1
    -        dataarray.append(date.minute)
    -
    -    minuteswithcount = [(x, dataarray.count(x)) for x in set(dataarray)]
    -    f.close()
    -    return minuteswithcount
    -

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

    -
    minuteswithcount = [(x, dataarray.count(x)) for x in set(dataarray)]
    -

    这样的数组以便于解析

    -
    [(0, 92), (1, 67), (2, 86), (3, 73), (4, 76), (5, 67), (6, 61), (7, 71), (8, 62), (9, 71), (10, 70), (11, 79), (12, 62), (13, 67), (14, 76), (15, 67), (16, 74), (17, 48), (18, 78), (19, 73), (20, 89), (21, 62), (22, 74), (23, 61), (24, 71), (25, 49), (26, 59), (27, 59), (28, 58), (29, 74), (30, 69), (31, 59), (32, 89), (33, 67), (34, 66), (35, 77), (36, 64), (37, 71), (38, 75), (39, 66), (40, 62), (41, 77), (42, 82), (43, 95), (44, 77), (45, 65), (46, 59), (47, 60), (48, 54), (49, 66), (50, 74), (51, 61), (52, 71), (53, 90), (54, 64), (55, 67), (56, 67), (57, 55), (58, 68), (59, 91)]
    -

    Matplotlib

    -

    开始之前需要安装``matplotlib

    -
    sudo pip install matplotlib
    -

    然后引入这个库

    -
      import matplotlib.pyplot as plt
    -

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

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

    最后代码可见

    -
    #!/usr/bin/env python
    -# -*- coding: utf-8 -*-
    -
    -import json
    -import dateutil.parser
    -import numpy as np
    -import matplotlib.mlab as mlab
    -import matplotlib.pyplot as plt
    -
    -
    -def parse_data(jsonfile):
    -    f = open(jsonfile, "r")
    -    dataarray = []
    -    datacount = 0
    -
    -    for line in open(jsonfile):
    -        line = f.readline()
    -        lin = json.loads(line)
    -        date = dateutil.parser.parse(lin["created_at"])
    -        datacount += 1
    -        dataarray.append(date.minute)
    -
    -    minuteswithcount = [(x, dataarray.count(x)) for x in set(dataarray)]
    -    f.close()
    -    return minuteswithcount
    -
    -
    -def draw_date(files):
    -    x = []
    -    y = []
    -    mwcs = parse_data(files)
    -    for mwc in mwcs:
    -        x.append(mwc[0])
    -        y.append(mwc[1])
    -
    -    plt.figure(figsize=(8,4))
    -    plt.plot(x, y,label = files)
    -    plt.legend()
    -    plt.show()
    -
    -draw_date("data/2014-01-01-0.json")
    -

    每周分析

    -

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

    -
    -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

    -
    def get_minutes_counts_with_id(jsonfile):
    -    datacount, dataarray = handle_json(jsonfile)
    -    minuteswithcount = [(x, dataarray.count(x)) for x in set(dataarray)]
    -    return minuteswithcount
    -
    -
    -def handle_json(jsonfile):
    -    f = open(jsonfile, "r")
    -    dataarray = []
    -    datacount = 0
    -
    -    for line in open(jsonfile):
    -        line = f.readline()
    -        lin = json.loads(line)
    -        date = dateutil.parser.parse(lin["created_at"])
    -        datacount += 1
    -        dataarray.append(date.minute)
    -
    -    f.close()
    -    return datacount, dataarray
    -
    -
    -def get_minutes_count_num(jsonfile):
    -    datacount, dataarray = handle_json(jsonfile)
    -    return datacount
    -
    -
    -def get_month_total():
    -    """
    -
    -    :rtype : object
    -    """
    -    monthdaycount = []
    -    for i in range(1, 20):
    -        if i < 10:
    -            filename = 'data/2014-02-0' + i.__str__() + '-0.json'
    -        else:
    -            filename = 'data/2014-02-' + i.__str__() + '-0.json'
    -        monthdaycount.append(get_minutes_count_num(filename))
    -    return monthdaycount
    -

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

    -

    Python Matplotlib图表

    -

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

    -
    if __name__ == '__main__':
    -    results = pd.get_month_total()
    -    print results
    -
    -    plt.figure(figsize=(8, 4))
    -    plt.plot(results.__getslice__(0, 7), label="first week")
    -    plt.plot(results.__getslice__(7, 14), label="second week")
    -    plt.plot(results.__getslice__(14, 21), label="third week")
    -    plt.legend()
    -    plt.show()
    -

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

    -

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

    -

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

    -

    存储到数据库中

    -

    SQLite3

    -

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

    -
    def init_db():
    -    conn = sqlite3.connect('userdata.db')
    -    c = conn.cursor()
    -    c.execute('''CREATE TABLE userinfo (owner text, language text, eventtype text, name text, url text)''')
    -

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

    -
    def get_count(username):
    -    count = 0
    -    userinfo = []
    -    condition = 'select * from userinfo where owener = \'' + str(username) + '\''
    -    for zero in c.execute(condition):
    -        count += 1
    -        userinfo.append(zero)
    -
    -    return count, userinfo
    -

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

    -
    (u'gmszone', u'ForkEvent', u'RESUME', u'TeX', u'https://github.com/gmszone/RESUME')
    -(u'gmszone', u'WatchEvent', u'iot-dashboard', u'JavaScript', u'https://github.com/gmszone/iot-dashboard')
    -(u'gmszone', u'PushEvent', u'wechat-wordpress', u'Ruby', u'https://github.com/gmszone/wechat-wordpress')
    -(u'gmszone', u'WatchEvent', u'iot', u'JavaScript', u'https://github.com/gmszone/iot')
    -(u'gmszone', u'CreateEvent', u'iot-doc', u'None', u'https://github.com/gmszone/iot-doc')
    -(u'gmszone', u'CreateEvent', u'iot-doc', u'None', u'https://github.com/gmszone/iot-doc')
    -(u'gmszone', u'PushEvent', u'iot-doc', u'TeX', u'https://github.com/gmszone/iot-doc')
    -(u'gmszone', u'PushEvent', u'iot-doc', u'TeX', u'https://github.com/gmszone/iot-doc')
    -(u'gmszone', u'PushEvent', u'iot-doc', u'TeX', u'https://github.com/gmszone/iot-doc')
    -109
    -

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

    -

    值得注意的是。

    -
    -rw-r--r--   1 fdhuang staff 905M Apr 12 14:59 userdata.db
    -

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

    -

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

    -
    brew install sqlite3
    -

    或者是

    -
    sudo port install sqlite3
    -

    或者是Ubuntu的

    -
    sudo apt-get install sqlite3
    -

    openSUSE自然就是

    -
    sudo zypper install sqlite3
    -

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

    -

    数据导入

    -

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

    -
    def handle_gzip_file(filename):
    -    userinfo = []
    -    with gzip.GzipFile(filename) as f:
    -        events = [line.decode("utf-8", errors="ignore") for line in f]
    -
    -        for n, line in enumerate(events):
    -            try:
    -                event = json.loads(line)
    -            except:
    -
    -                continue
    -
    -            actor = event["actor"]
    -            attrs = event.get("actor_attributes", {})
    -            if actor is None or attrs.get("type") != "User":
    -                continue
    -
    -            key = actor.lower()
    -
    -            repo = event.get("repository", {})
    -            info = str(repo.get("owner")), str(repo.get("language")), str(event["type"]), str(repo.get("name")), str(
    -                repo.get("url"))
    -            userinfo.append(info)
    -
    -    return userinfo
    -
    -def build_db_with_gzip():
    -    init_db()
    -    conn = sqlite3.connect('userdata.db')
    -    c = conn.cursor()
    -
    -    year = 2014
    -    month = 3
    -
    -    for day in range(1,31):
    -        date_re = re.compile(r"([0-9]{4})-([0-9]{2})-([0-9]{2})-([0-9]+)\.json.gz")
    -
    -        fn_template = os.path.join("march",
    -                                   "{year}-{month:02d}-{day:02d}-{n}.json.gz")
    -        kwargs = {"year": year, "month": month, "day": day, "n": "*"}
    -        filenames = glob.glob(fn_template.format(**kwargs))
    -
    -        for filename in filenames:
    -            c.executemany('INSERT INTO userinfo VALUES (?,?,?,?,?)', handle_gzip_file(filename))
    -
    -    conn.commit()
    -    c.close()
    -

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

    -

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

    -

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

    -

    首先是正规匹配

    -
    date_re = re.compile(r"([0-9]{4})-([0-9]{2})-([0-9]{2})-([0-9]+)\.json.gz")
    -

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

    -
    -

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

    -
    -

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

    -

    最后代码可以见

    -

    github.com/gmszone/ml

    -

    更好的方案?

    -

    Redis

    -

    查询用户事件总数

    -
    import redis
    -r = redis.StrictRedis(host='localhost', port=6379, db=0)
    -pipe = pipe = r.pipeline()
    -pipe.zscore('osrc:user',"gmszone")
    -pipe.execute()
    -

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

    -
    >>> pipe.zscore('osrc:user',"dfm")
    -<redis.client.StrictPipeline object at 0x104fa7f50>
    ->>> pipe.execute()
    -[425.0]
    ->>>
    -

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

    -
    >>> pipe.hgetall('osrc:user:gmszone:day')
    -<redis.client.StrictPipeline object at 0x104fa7f50>
    ->>> pipe.execute()
    -[{'1': '51', '0': '41', '3': '17', '2': '34', '5': '28', '4': '22', '6': '34'}]
    -

    结果大致如下图所示:

    -
    -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 查询

    -

    主要代码如下所示

    -
    def get_vector(user, pipe=None):
    -
    -    r = redis.StrictRedis(host='localhost', port=6379, db=0)
    -    no_pipe = False
    -    if pipe is None:
    -        pipe = pipe = r.pipeline()
    -        no_pipe = True
    -
    -    user = user.lower()
    -    pipe.zscore(get_format("user"), user)
    -    pipe.hgetall(get_format("user:{0}:day".format(user)))
    -    pipe.zrevrange(get_format("user:{0}:event".format(user)), 0, -1,
    -                   withscores=True)
    -    pipe.zcard(get_format("user:{0}:contribution".format(user)))
    -    pipe.zcard(get_format("user:{0}:connection".format(user)))
    -    pipe.zcard(get_format("user:{0}:repo".format(user)))
    -    pipe.zcard(get_format("user:{0}:lang".format(user)))
    -    pipe.zrevrange(get_format("user:{0}:lang".format(user)), 0, -1,
    -                   withscores=True)
    -
    -    if no_pipe:
    -        return pipe.execute()
    -

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

    -
    [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中用于解析的代码

    -
    def parse_vector(results):
    -    points = np.zeros(nvector)
    -    total = int(results[0])
    -
    -    points[0] = 1.0 / (total + 1)
    -
    -    # Week means.
    -    for k, v in results[1].iteritems():
    -        points[1 + int(k)] = float(v) / total
    -
    -    # Event types.
    -    n = 8
    -    for k, v in results[2]:
    -        points[n + evttypes.index(k)] = float(v) / total
    -
    -    # Number of contributions, connections and languages.
    -    n += nevts
    -    points[n] = 1.0 / (float(results[3]) + 1)
    -    points[n + 1] = 1.0 / (float(results[4]) + 1)
    -    points[n + 2] = 1.0 / (float(results[5]) + 1)
    -    points[n + 3] = 1.0 / (float(results[6]) + 1)
    -
    -    # Top languages.
    -    n += 4
    -    for k, v in results[7]:
    -        if k in langs:
    -            points[n + langs.index(k)] = float(v) / total
    -        else:
    -            # Unknown language.
    -            points[-1] = float(v) / total
    -
    -    return points
    -

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

    -
    def get_points(usernames):
    -    r = redis.StrictRedis(host='localhost', port=6379, db=0)
    -    pipe = r.pipeline()
    -
    -    results = get_vector(usernames)
    -    points = np.zeros([len(usernames), nvector])
    -    points = parse_vector(results)
    -    return 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.        ]
    -

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

    -

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

    -
    -

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

    -
    -

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

    -
      -
    • clone某个项目的代码到本地
    • -
    • 查看这个项目的release列表
    • -
    • 找到一个看得懂的release版本,如1.0或者更早的版本
    • -
    • 读懂上一个版本的代码
    • -
    • 向后阅读大版本的源码
    • -
    • 读最新的源码
    • -
    -

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

    -

    阅读过程

    -

    在我阅读的前端库、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)完全注释》,简单地再介绍一下: - 版本0.00是一个hello,world程序 - 版本0.01包含了可以工作的代码 - 版本0.11是基本可以正常的版本

    -

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

    -

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

    -

    因此,我们可以得到几个简单的结论: - 我们需要阅读最早的有核心代码的版本 - 我们需要阅读1.0版本的Release - 往后每一次大的Release我们都需要了解一下

    -

    示例

    -

    以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
    -
    -

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

    -
      -
    • 0.1版本
    • -
    • 。。。
    • -
    • 最新的0.10.1版本
    • -

    GitHub连击

    100天

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

    @@ -2411,10 +2084,10 @@ pipe.execute()

    编程的基础能力

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

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

    -

    重构

    +

    重构

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

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

    -

    测试

    +

    测试

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

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

    @@ -2633,5 +2306,442 @@ pipe.execute()

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

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

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

    +

    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都有。

    +

    数据解析

    +
    import json
    +for line in open(jsonfile):
    +    line = f.readline()
    +

    然后再解析json

    +
    import dateutil.parser
    +
    +lin = json.loads(line)
    +date = dateutil.parser.parse(lin["created_at"])
    +

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

    +
    def parse_data(jsonfile):
    +    f = open(jsonfile, "r")
    +    dataarray = []
    +    datacount = 0
    +
    +    for line in open(jsonfile):
    +        line = f.readline()
    +        lin = json.loads(line)
    +        date = dateutil.parser.parse(lin["created_at"])
    +        datacount += 1
    +        dataarray.append(date.minute)
    +
    +    minuteswithcount = [(x, dataarray.count(x)) for x in set(dataarray)]
    +    f.close()
    +    return minuteswithcount
    +

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

    +
    minuteswithcount = [(x, dataarray.count(x)) for x in set(dataarray)]
    +

    这样的数组以便于解析

    +
    [(0, 92), (1, 67), (2, 86), (3, 73), (4, 76), (5, 67), (6, 61), (7, 71), (8, 62), (9, 71), (10, 70), (11, 79), (12, 62), (13, 67), (14, 76), (15, 67), (16, 74), (17, 48), (18, 78), (19, 73), (20, 89), (21, 62), (22, 74), (23, 61), (24, 71), (25, 49), (26, 59), (27, 59), (28, 58), (29, 74), (30, 69), (31, 59), (32, 89), (33, 67), (34, 66), (35, 77), (36, 64), (37, 71), (38, 75), (39, 66), (40, 62), (41, 77), (42, 82), (43, 95), (44, 77), (45, 65), (46, 59), (47, 60), (48, 54), (49, 66), (50, 74), (51, 61), (52, 71), (53, 90), (54, 64), (55, 67), (56, 67), (57, 55), (58, 68), (59, 91)]
    +

    Matplotlib

    +

    开始之前需要安装``matplotlib

    +
    sudo pip install matplotlib
    +

    然后引入这个库

    +
      import matplotlib.pyplot as plt
    +

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

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

    最后代码可见

    +
    #!/usr/bin/env python
    +# -*- coding: utf-8 -*-
    +
    +import json
    +import dateutil.parser
    +import numpy as np
    +import matplotlib.mlab as mlab
    +import matplotlib.pyplot as plt
    +
    +
    +def parse_data(jsonfile):
    +    f = open(jsonfile, "r")
    +    dataarray = []
    +    datacount = 0
    +
    +    for line in open(jsonfile):
    +        line = f.readline()
    +        lin = json.loads(line)
    +        date = dateutil.parser.parse(lin["created_at"])
    +        datacount += 1
    +        dataarray.append(date.minute)
    +
    +    minuteswithcount = [(x, dataarray.count(x)) for x in set(dataarray)]
    +    f.close()
    +    return minuteswithcount
    +
    +
    +def draw_date(files):
    +    x = []
    +    y = []
    +    mwcs = parse_data(files)
    +    for mwc in mwcs:
    +        x.append(mwc[0])
    +        y.append(mwc[1])
    +
    +    plt.figure(figsize=(8,4))
    +    plt.plot(x, y,label = files)
    +    plt.legend()
    +    plt.show()
    +
    +draw_date("data/2014-01-01-0.json")
    +

    每周分析

    +

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

    +
    +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

    +
    def get_minutes_counts_with_id(jsonfile):
    +    datacount, dataarray = handle_json(jsonfile)
    +    minuteswithcount = [(x, dataarray.count(x)) for x in set(dataarray)]
    +    return minuteswithcount
    +
    +
    +def handle_json(jsonfile):
    +    f = open(jsonfile, "r")
    +    dataarray = []
    +    datacount = 0
    +
    +    for line in open(jsonfile):
    +        line = f.readline()
    +        lin = json.loads(line)
    +        date = dateutil.parser.parse(lin["created_at"])
    +        datacount += 1
    +        dataarray.append(date.minute)
    +
    +    f.close()
    +    return datacount, dataarray
    +
    +
    +def get_minutes_count_num(jsonfile):
    +    datacount, dataarray = handle_json(jsonfile)
    +    return datacount
    +
    +
    +def get_month_total():
    +    """
    +
    +    :rtype : object
    +    """
    +    monthdaycount = []
    +    for i in range(1, 20):
    +        if i < 10:
    +            filename = 'data/2014-02-0' + i.__str__() + '-0.json'
    +        else:
    +            filename = 'data/2014-02-' + i.__str__() + '-0.json'
    +        monthdaycount.append(get_minutes_count_num(filename))
    +    return monthdaycount
    +

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

    +

    Python Matplotlib图表

    +

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

    +
    if __name__ == '__main__':
    +    results = pd.get_month_total()
    +    print results
    +
    +    plt.figure(figsize=(8, 4))
    +    plt.plot(results.__getslice__(0, 7), label="first week")
    +    plt.plot(results.__getslice__(7, 14), label="second week")
    +    plt.plot(results.__getslice__(14, 21), label="third week")
    +    plt.legend()
    +    plt.show()
    +

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

    +

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

    +

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

    +

    存储到数据库中

    +

    SQLite3

    +

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

    +
    def init_db():
    +    conn = sqlite3.connect('userdata.db')
    +    c = conn.cursor()
    +    c.execute('''CREATE TABLE userinfo (owner text, language text, eventtype text, name text, url text)''')
    +

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

    +
    def get_count(username):
    +    count = 0
    +    userinfo = []
    +    condition = 'select * from userinfo where owener = \'' + str(username) + '\''
    +    for zero in c.execute(condition):
    +        count += 1
    +        userinfo.append(zero)
    +
    +    return count, userinfo
    +

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

    +
    (u'gmszone', u'ForkEvent', u'RESUME', u'TeX', u'https://github.com/gmszone/RESUME')
    +(u'gmszone', u'WatchEvent', u'iot-dashboard', u'JavaScript', u'https://github.com/gmszone/iot-dashboard')
    +(u'gmszone', u'PushEvent', u'wechat-wordpress', u'Ruby', u'https://github.com/gmszone/wechat-wordpress')
    +(u'gmszone', u'WatchEvent', u'iot', u'JavaScript', u'https://github.com/gmszone/iot')
    +(u'gmszone', u'CreateEvent', u'iot-doc', u'None', u'https://github.com/gmszone/iot-doc')
    +(u'gmszone', u'CreateEvent', u'iot-doc', u'None', u'https://github.com/gmszone/iot-doc')
    +(u'gmszone', u'PushEvent', u'iot-doc', u'TeX', u'https://github.com/gmszone/iot-doc')
    +(u'gmszone', u'PushEvent', u'iot-doc', u'TeX', u'https://github.com/gmszone/iot-doc')
    +(u'gmszone', u'PushEvent', u'iot-doc', u'TeX', u'https://github.com/gmszone/iot-doc')
    +109
    +

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

    +

    值得注意的是。

    +
    -rw-r--r--   1 fdhuang staff 905M Apr 12 14:59 userdata.db
    +

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

    +

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

    +
    brew install sqlite3
    +

    或者是

    +
    sudo port install sqlite3
    +

    或者是Ubuntu的

    +
    sudo apt-get install sqlite3
    +

    openSUSE自然就是

    +
    sudo zypper install sqlite3
    +

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

    +

    数据导入

    +

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

    +
    def handle_gzip_file(filename):
    +    userinfo = []
    +    with gzip.GzipFile(filename) as f:
    +        events = [line.decode("utf-8", errors="ignore") for line in f]
    +
    +        for n, line in enumerate(events):
    +            try:
    +                event = json.loads(line)
    +            except:
    +
    +                continue
    +
    +            actor = event["actor"]
    +            attrs = event.get("actor_attributes", {})
    +            if actor is None or attrs.get("type") != "User":
    +                continue
    +
    +            key = actor.lower()
    +
    +            repo = event.get("repository", {})
    +            info = str(repo.get("owner")), str(repo.get("language")), str(event["type"]), str(repo.get("name")), str(
    +                repo.get("url"))
    +            userinfo.append(info)
    +
    +    return userinfo
    +
    +def build_db_with_gzip():
    +    init_db()
    +    conn = sqlite3.connect('userdata.db')
    +    c = conn.cursor()
    +
    +    year = 2014
    +    month = 3
    +
    +    for day in range(1,31):
    +        date_re = re.compile(r"([0-9]{4})-([0-9]{2})-([0-9]{2})-([0-9]+)\.json.gz")
    +
    +        fn_template = os.path.join("march",
    +                                   "{year}-{month:02d}-{day:02d}-{n}.json.gz")
    +        kwargs = {"year": year, "month": month, "day": day, "n": "*"}
    +        filenames = glob.glob(fn_template.format(**kwargs))
    +
    +        for filename in filenames:
    +            c.executemany('INSERT INTO userinfo VALUES (?,?,?,?,?)', handle_gzip_file(filename))
    +
    +    conn.commit()
    +    c.close()
    +

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

    +

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

    +

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

    +

    首先是正规匹配

    +
    date_re = re.compile(r"([0-9]{4})-([0-9]{2})-([0-9]{2})-([0-9]+)\.json.gz")
    +

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

    +
    +

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

    +
    +

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

    +

    最后代码可以见

    +

    github.com/gmszone/ml

    +

    更好的方案?

    +

    Redis

    +

    查询用户事件总数

    +
    import redis
    +r = redis.StrictRedis(host='localhost', port=6379, db=0)
    +pipe = pipe = r.pipeline()
    +pipe.zscore('osrc:user',"gmszone")
    +pipe.execute()
    +

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

    +
    >>> pipe.zscore('osrc:user',"dfm")
    +<redis.client.StrictPipeline object at 0x104fa7f50>
    +>>> pipe.execute()
    +[425.0]
    +>>>
    +

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

    +
    >>> pipe.hgetall('osrc:user:gmszone:day')
    +<redis.client.StrictPipeline object at 0x104fa7f50>
    +>>> pipe.execute()
    +[{'1': '51', '0': '41', '3': '17', '2': '34', '5': '28', '4': '22', '6': '34'}]
    +

    结果大致如下图所示:

    +
    +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 查询

    +

    主要代码如下所示

    +
    def get_vector(user, pipe=None):
    +
    +    r = redis.StrictRedis(host='localhost', port=6379, db=0)
    +    no_pipe = False
    +    if pipe is None:
    +        pipe = pipe = r.pipeline()
    +        no_pipe = True
    +
    +    user = user.lower()
    +    pipe.zscore(get_format("user"), user)
    +    pipe.hgetall(get_format("user:{0}:day".format(user)))
    +    pipe.zrevrange(get_format("user:{0}:event".format(user)), 0, -1,
    +                   withscores=True)
    +    pipe.zcard(get_format("user:{0}:contribution".format(user)))
    +    pipe.zcard(get_format("user:{0}:connection".format(user)))
    +    pipe.zcard(get_format("user:{0}:repo".format(user)))
    +    pipe.zcard(get_format("user:{0}:lang".format(user)))
    +    pipe.zrevrange(get_format("user:{0}:lang".format(user)), 0, -1,
    +                   withscores=True)
    +
    +    if no_pipe:
    +        return pipe.execute()
    +

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

    +
    [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中用于解析的代码

    +
    def parse_vector(results):
    +    points = np.zeros(nvector)
    +    total = int(results[0])
    +
    +    points[0] = 1.0 / (total + 1)
    +
    +    # Week means.
    +    for k, v in results[1].iteritems():
    +        points[1 + int(k)] = float(v) / total
    +
    +    # Event types.
    +    n = 8
    +    for k, v in results[2]:
    +        points[n + evttypes.index(k)] = float(v) / total
    +
    +    # Number of contributions, connections and languages.
    +    n += nevts
    +    points[n] = 1.0 / (float(results[3]) + 1)
    +    points[n + 1] = 1.0 / (float(results[4]) + 1)
    +    points[n + 2] = 1.0 / (float(results[5]) + 1)
    +    points[n + 3] = 1.0 / (float(results[6]) + 1)
    +
    +    # Top languages.
    +    n += 4
    +    for k, v in results[7]:
    +        if k in langs:
    +            points[n + langs.index(k)] = float(v) / total
    +        else:
    +            # Unknown language.
    +            points[-1] = float(v) / total
    +
    +    return points
    +

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

    +
    def get_points(usernames):
    +    r = redis.StrictRedis(host='localhost', port=6379, db=0)
    +    pipe = r.pipeline()
    +
    +    results = get_vector(usernames)
    +    points = np.zeros([len(usernames), nvector])
    +    points = parse_vector(results)
    +    return 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.        ]
    +

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