全栈工程师就是一棵歪脖子树

一棵歪脖子树

  老张有一个林场,专门种植树木,待树木成材后销到城里做家具。林场里有不少杉树,长得都非常挺拔,偏偏不知怎的长了一棵歪脖子树,长得倒是郁郁葱葱,偏偏造型奇特,别的树都力争上油,可这棵歪脖子树不仅横着长,还长得颇为怪异。老张几次都想挖掉它当柴火烧,看它那怪异的造型又忍住了,心想等这批杉 树成材,一起砍掉吧。

  一到晚上夜深人静的时候,林场里就炸开了锅,杉树们极力取笑歪脖子树。“你知道作为一棵树,我们的终极目标就是成为有用之材吗,你看旁边那个,可以做梁柱,小歪脖子,你丫能干啥啊?”;“这个臭歪脖子树,抢了我们的养分和阳光,长成这样,完全是丢我们的脸,我呸!”.

  歪脖子树有自己的梦想,它要长成它喜欢的造型,如此日复一日,林场里的杉树渐渐长大了,老张决定逐步卖掉这些杉树。随着一批批杉树被运出去,奚落与 嘲笑更多了。“瞧睢人家,你左边的那棵,现在在被打造成前台,在知名大企业,右边的那棵更牛逼了,它被做成了老板桌,你知道天天用的老板是谁吗,说出来吓 死你。你个臭傻逼,作为一棵树,你居然不向上长?”

  林场里的树越来越少了,歪脖子树也有些失落,对自己的未来产生了怀疑,当初为什么不和他们一样长呢。

  有一天,一个老板和他弟弟过来买树,他弟弟是个园艺家。园艺家正在筹划一个大型的园林,一切都弄好了,唯独还差一棵造型奇特的树作为点睛之用,他找了很多地方,不是造型不满意就是人为痕迹太过于严重。终于,他看到了这棵歪脖子树,立马柏板重金购买。

  老张觉得不可思议,林场里的杉树们更是炸开了锅,“凭什么啊,我们长这么高,它就那么点高,还长得这么怪,凭啥卖了这么高的价钱”。原来,随着经济条件的的变好,人们有闲钱了,也愿意花钱去欣赏园艺了,长得高的树木很多,但长得怪的树木却是极为难得。

  有些人一听这个全字满腔怒火,凭啥你的职业中带一个全字,你是要前端后端通吃吗,还要吃掉移动端,你是不是要抢我的饭碗。你凭什么啊,做技术就应该专精,你知道茴香豆的茴字有几种写法吗?

  记得三年前我在一篇文章《两个重要而又容易被忽视的角色》中有谈到前端工程师和产品工程师的重要性,结果有一大波后端工程师批评我,他们认为前端就是一个低端的职位,切切图写写JS就完事了,重要个屁啊。现在你来看,到处都在招前端,而且工资不比后端低,甚至略高,还不一定能请到人。

  全端工程师不是什么高大上,它只是一种职业,和DBA运维产品工程师一样,只是职业的细分。全端工程师不是要吃掉前端更不是要吃掉后端,它是环境发 生到一情况况所催生的一种新的职业,它恰恰是市场更细分的结果。我能理解某些工程师的那种紧张与孤傲,早些年出现前端吞掉我们的一部分工作,现在又要炒全 端吃掉我们的一部分工作,你让我们这些垂直发展的人怎么活,连茴字有几种写法你都不知道,你有什么资格拿着和我们一样的工资?

  与新技术的任何方面打交道的人,他们确信自己是在做天生的高科技业务……在我们两人看来,他们一般都不是。在这些领域中, 那些有根本性突破的研究人员是在做高科技业务。我们所有其他局外人只是他们工作成果的应用者。我们用计算机和其它新技术组件来开发我们的产品或者组织我们 的事务——《人件》

  很抱歉,不管你同不同意,我们大多数人都是技术的使用者,除了极少数的牛人之外,我们都在为产品服务。我们的客户并不想知道你有多么牛逼的算法,也不想知道你用了多少种设计模式,他们要的是,你能给我带来什么价值,能不能让我用得爽用得值。

一幢房子

  我很喜欢把软件工程与建筑业相比,我很抱歉又把大家和农民工相提并论了,其实我们与他们,并没有本质上的区别,他们在搬砖,而我们在搬bit,他们 晒着太阳累一点活得长一点,咱们吹着空调死得早一点。做得好一点的工程师,无非就是一个代码工匠,我们都是手艺人,手熟而已,无它。

  垂直发展是钢筋,而横向发展可能就是水泥。没有钢筋的房子建不高,没有水泥的房子也是有的,不过水泥+钢筋还是多数了吧。无需你自己是钢筋就指责水 泥,就象Node.js刚出来的时候,被人指责是一班臭前端搞出来玩的的东西。任何技术都有它的适用场景,离开某个场景可能一文不值,人也一样。

  不要因为自己用Java就批评ASP.net,也无需自己开发Android就骂果粉,我知道,PHP是全世界最好的语言。如果我们能在各自喜欢的领域有更好的发展,不是更好么。你觉得横向发展不好,你不跟就是,他强由他强,清风拂山冈;他横由他横,明月照大江。

为什么会需要全端工程师

我们需要有全局视野的人

  老板们都说,开发人员要有产品意识,要有用户意识,如果你只做后端,恐怕你是不能理解前端对用户的重要性。其实说来惭愧,很多人误以为我是个臭前 端,其实我从来没有专职做过前端,我很抱歉我站错了队。这么多年,我一直是在写点前端并厚着脸皮混在后端,作为一个低水平的臭后端,我只是更喜欢和用户接 近一点而已。

  IT行业的鄙视链是很严重的,大家之间相互鄙视,或者,你换个岗去做对方的工作,我相信你就能了解到他们的工作价值了。前端说用户体验重要,后端说没有后端你前端屁都不是,彼此对调一下,你能感受更多。

  一种技术不能解决所有问题,我们需要从多种技术中权衡,到底是用Hybrid还是用Native,这是要根据你的业务场景和人员配置情况来判断的,不是说看别人写的几篇教程或者指南就能决定的。

创业公司越来越多

  IT的创业公司越来越多,除了少数真正玩技术的公司,恐怕还是要玩产品的。既然是玩产品,就免不了各种设备各种平台各种兼容。新公司的资源总是有限 的,单个工种的工作量又没那么饱和。所以有一个多面手在创业初期是很有帮助的,不可能为了一个iOS的客户端去专门请一个月薪上万的人来做iOS应用,再 花上万的月薪找人来做Android客户端,省钱是创业公司的主流,但又希望自己的产品能全面铺开,所以全端工程师是不二之选。

硬件设备的发展

  有人一定会说,硬件设备和全端工程师有半毛钱关系,还真有那么一些关系。软件最初的时候是单机版,要求的只是软件工程师,后来因为网速的提升与网络 的普及,B/S取代了C和C/S,所以就演变成了后端工程师占主流地位。到现在,客户的硬件设备越来越强大,网速也越来越快,所以Web前端会占主流,而 所谓的后端会越来越萎缩。不管你愿意不愿意,我认为,未来的后端,会发展到只提供Web API的数据,只需要少数工程师就能搞定。大部工作会由所谓的前端和各种客户端来完成,这就是我讲的后端已死。

  移动设备的发展和云的概念以及第三方平台,肯定对这种需求起到了推波助澜的作用。一方面,企业需要一个能掌握多种技术的人以降低成本,另一方面,也需要一个有全局视野的人来参与,他需要对产品中用到的多种技术都熟悉,所以,全端工程有时候会是一个胶水的作用。

结语

  全端工程师不会替代后端工程师,也不会替代替代前端工程师,这个职业不是要替代谁,它只是让我们更好的工作,只是一种新的职业而已。胶水有胶水的价值,催化剂有催化剂用处。积极地拥抱变化吧,唯一不变的是变化的本身,你我都无法阻止。

  专精是一个方向,横向也是一个方向,没有谁比谁厉害。不用担心别人呼吸你就没有氧气了,坚持自己的发展方向,你总会有价值的。尺有所长寸有所短,不 必因为彼此的发展方向不一就大动干戈,有这力气,还是多搬个砖吧,多动一下,总是要活得长一点的。我知道Master Wugui是不会同意我这个看法的。

全端工程师,就是掌握多种技能,并能利用多种技能独立完成产品的人——涂雅

PHP编码规范

编码规范

文件编码统一为:UTF-8,请开发人员调整编辑器的文件编码为UTF-8,并关闭UTF-8 BOM的功能。请不要使用windows自带的记事本编辑项目文件。

命名规范

类命名

使用大驼峰法命名,即每个单词的首字母均大写;

class SimpleDB {

}

函数命名

使用小驼峰法命名,即第一个单词首字母小写,其余单词首字母均大写;

public function __construct($downloadPath = '') {

}

变量命名

使用小驼峰法命名,即第一个单词首字母小写,其余单词首字母均大写;

protected $callbackFunctions;

如为私有变量,请在变量名前方加上下划线。

private $_adapter;

常量命名

所有字母均大写,前后加上双下划线,单词之间用下划线分割;如果为ThinkSNS内部常量,则使用tsdefine();

define('SITE_PATH', dirname(__FILE__));
tsdefine('IS_HTTPS', 0);

注释规范

文件头部注释

文件头部注释主要用来阐述此文件的作用,作者,版本。按照以下形式书写:

<?php
/**
 * 用户认证模型 - 数据对象模型
 * @author Yuwenhui <yuwenhui1986@gmail.com>
 * @version $Id$
 */

其中@author为作者的名称,后面<>中为电子邮箱;@version定义为$Id$是为了匹配svn的关键字。

引用文件和定义常量注释

文件的引用和常量的定义一般都放置在文件的开头部分。采用双斜线注释法(//)。

// 定义MySQL查询输出封装为多结果,必须用循环处理
define('CLIENT_MULTI_RESULTS', 131072);

// 加载上传操作类
require_once SITE_PATH.'/addons/library/UploadFile.class.php';

多行注释,使用如下形式:

/**
 * Cookie 设置、获取、清除 (支持数组或对象直接设置) 2009-07-9
 * 1 获取cookie: cookie('name')
 * 2 清空当前设置前缀的所有cookie: cookie(null)
 * 3 删除指定前缀所有cookie: cookie(null,'think_') | 注:前缀将不区分大小写
 * 4 设置cookie: cookie('name','value') | 指定保存时间: cookie('name','value',3600)
 * 5 删除cookie: cookie('name',null)
 * $option 可用设置prefix,expire,path,domain
 * 支持数组形式:cookie('name','value',array('expire'=>1,'prefix'=>'think_'))
 * 支持query形式字符串:cookie('name','value','prefix=tp_&expire=10000')
 * 2010-1-17 去掉自动序列化操作,兼容其他语言程序。
 */

类(接口)注释

一个类(接口)在声明的时候必须声明其作用。

/**
 * 功能升级类
 * 功能1:
 * 功能2:
 * @author 水上铁 <476974493@qq.com>
 * @version $Id$
 */
class Image {

}

注释第一行声明类的作用,后面为类实现的主要功能;@author为作者的名称,后面<>中为电子邮箱;@version定义为$Id$是为了匹配svn的关键字。

函数注释

函数的注释包括函数作用、参数和返回值。注意如果是无返回的函数,必须指明@return void。

/**
 * 获取语言配置内容列表
 * @param array $map 查询条件
 * @return array 语言配置内容列表
 */
public function getLangContent($map) {

}

/**
 * 更改语言配置内容
 * @param array $data 语言配置内容
 * @param integer $sid 语言资源ID
 * @return integer 是否更改成功,1表示成功;0表示失败
 */
public function updateLangData($data, $sid) {

}

注释第一行声明函数的作用;@param指函数的参数;@return是函数的返回值。

程序行间注释

行间注释采用双斜线注释法

// 更新缓存文件
 $this->createCacheFile($addData['appname'], $addData['filetype']);

书写规范

对齐和缩进

每个缩进的单位约定是一个TAB(4个空白字符宽度,而不是4个空格),请每个开发人员在编辑器(UltraEdit、EditPlus、Eclipse、Sublime Text等)中进行强制设定,以防在编写代码时遗忘而造成格式上的不规范。

大括号{}、if和switch

首括号 { 与关键词同行,尾括号 } 与关键字同列;

if($condition) {
    switch($var) {
        case 1: echo 'var is 1'; break;
        case 2: echo 'var is 2'; break;
        default: echo 'var is neither 1 or 2'; break;
    }
}

if结构中,else和elseif与前后两个大括号同行,左右各一个空格。另外,即便if后只有一行语句,仍然需要加入大括号,以保证结构清晰;

if($condition) {
    $var++;
} else {
    $var--;
}

switch结构中,通常当一个case块处理后,将跳过之后的case块处理,因此大多数情况下需要添加break。break的位置视程序逻辑,与case同在一行,或新起一行均可,但同一switch体中,break的位置格式应当保持一致。

if($condition) {
    switch($var) {
        case 1: echo 'var is 1'; break;
        case 2: echo 'var is 2'; break;
        default: echo 'var is neither 1 or 2'; break;
    }
} else {
    switch($str) {
        case 'abc':
            $result = 'abc';
            break;
        default:
            $result = 'unknown';
            break;
    }
}

运算符、小括号、关键词和函数

每个运算符与两边参与运算的值或表达式中间要有一个空格,唯一的特例是字符连接运算符号 . 两边不加空格;

$result = (($a + 1) * 3 / 2 + $num)).'Test';
$condition ? func1($var) : func2($var);

左括号 ( 应和函数关键词紧贴在一起;右括号 ) 除后面是 ) 或者 . 以外,其他一律用空格隔开它们;

function fun1($var1, $var2) {

}

exit(json_encode($data));

每段较大的程序体,上、下应当加入空白行,两个程序块之间只使用1个空行,禁止使用多行。少于15行的程序块,可不加上下空白行。

函数的缩进

  • 函数参数的命名和变量的命名规范一致;
  • 函数定义中的左小括号 ( ,与函数名紧挨,中间无需空格;
  • 开始的左大括号与函数定义为同一行,中间加一个空格,不要另起一行;
  • 具有默认值的参数应该位于参数列表的后面;
  • 函数调用与定义的时候参数与参数之间加入一个空格;
  • 必须仔细检查并切实杜绝函数起始缩进位置与结束缩进位置不同的现象;

符合规范的定义:

public function index($string, $operation, $key = '') {
    //函数体
}

不符合规范的定义:

public function authcode ($string,$key='',$operation)
{
    //函数体
}

引号

PHP中单引号和双引号具有不同的含义,最大的几项区别如下:

  • 单引号中,任何变量($var)、特殊转义字符(如 \t \r \n 等)不会被解析,因此PHP的解析速度更快,转义字符仅仅支持 \'\\ 这样对单引号和反斜杠本身的转义;
  • 双引号中,变量($var)值会代入字符串中,特殊转义字符也会被解析成特定的单个字符,这样虽然程序编写更加方便,但同时PHP的解析也很慢;
  • 数组中,如果下标不是整型,而是字符串类型,请务必用单引号将下标括起,正确的写法为$array['key'],而不是$array[key];因为不正确的写法会使PHP解析器认为key是一个常量,进而先判断常量是否存在,不存在时才以 key 作为下标带入表达式中,同时出发错误事件,产生一条Notice级错误。

因此,在绝大多数可以使用单引号的场合,禁止使用双引号。依据上述分析,可以或必须使用单引号的情况包括但不限于下述:

  • 字符串为固定值,不包含 \t 等特殊转义字符;
  • 数组的固定下标,例如:$array['key'];
  • 表达式中不需要带入变量,例如:$string = 'test';, 而非:$string = "test$var";
  • 数据库SQL语句中,所有数据必须加单引号,无论数值还是字串,以避免可能的注入漏洞和SQL错误。

Javascript编码规范

命名规范

通用命名规则

  • 所有变量必须是有意义的英文,严厉禁止拼音;
  • 变量命名采用小驼峰法(第一个单词首字母小写,其余单词首字母大写);
  • 变量允许使用公认英文缩写,例如nav
  • 常量必须所有单词大写,并且每个单词间加下划线;
  • 类命名必须是大驼峰法(所有单词第一个字母均大写);
  • 私有类的变量属性成员, 建议使用混合式命名,并前面下下划线;
  • “on”只能用作事件的命名;
  • 所有全局变量必须初始化;
  • 保留字以及特有的dom属性不能作为变量名。

变量命名规范

类型前缀 + 有意义的单词

  • 字符串:sXXX,如:sName,sHtml;
  • 数字:nXXX,如:nPage,nTotal;
  • 逻辑:bXXX,如:bChecked,bHasLogin;
  • 数组:aXXX,如:aList,aGroup;
  • 正则:rXXX,如:rDomain,rEmail;
  • 函数:fXXX,如:fGetList;
  • DOM节点:dXX,如:dDiv,dSpan;
  • 其他类型:oXXX,如:oButton,oDate;
  • 特殊简写:小范围作用域临时变量,如函数内部的局部变量或参数:o(Object)、e(Element)、evt(event)、err(errot)等;
  • 循环变量:i、j、k以此类推;

函数命名规范

  • 普通函数:动词+名词,如:fGetList、fGetVersion;
  • 涉及逻辑返回值的函数:is、has、can,如:fisAdmin、fhasChild;
  • 内部函数:_f+上面规则,如:fLoopCount;

书写规范

对齐和缩进

  • 必须使用 Tab 键进行代码缩进,以节约代码大小,建议设置编辑器的tab为4个空格的宽度(而不是4个空格);
  • 所有语句结束后,必须使用 ; 号结束;
  • 大括号前面不能换行;
  • 操作符必须使用空格隔开;

语法结构

普通代码段应该如下:

while(!isDone) {
    doSomething();
    isDone = moreToDo();
}

变量定义方法如下:

var a = null;
var b = 1;
var c = 0;

函数定义方法如下:

var funcA = function() {
    var a = 0;
    ...
}

if 语句应该像这样:

if(someCondition) {
    statements;
} else if(someOtherCondition) {
    statements;
} else {
    statements;
}

for 语句应该像这样:

for(initialization; condition; update) {
    statements;
}

while 语句应该像这样:

while(!isDone) {
    doSomething();
    isDone = moreToDo();
}

do … while 语句应该像这样:

do {
    statements;
} while(condition);

switch 语句应该像这样:

switch(condition) {
    case "A":
        statements;
        break;
    case "B":
        statements;
        break;
    default:
        statements;
        break;
}

try … catch 语句应该像这样:

try {
    statements;
} catch(ex) {
    statements;
} finally {
    statements;
}

单行的 if - else,while 或者 for 语句也必须加入括号:

if(condition) {
    statement;
}

while(condition) {
    statement;
}

for(intialization; condition; update) {
    statement;
}

Javascript编写风格

对象定义规范

在Javascript中,所有类型都是对象,包括string类型。函数的定义,统一使用以下方式:

var a = function() {...};

最后要加上 “;”号,不要再使用以下方式:

function a() {}

对象定义规范:

  • string类型定义:var str = 'xxxxxx';
  • function类型定义:var func = function() {...};
  • array类型定义:var arr = new Array();
  • object类型定义:var obj = {'name': 'xxxxxx', 'age': '10'}

事件监听

开发中统一使用module.js对对象事件(DOM节点)进行监听。

块状节点:model-node,包括的标签:div、ul、li、dl、form等,例如:

<div model-node="testmodel" model-args="name=testname&value=testvalue"></div>

独立事件节点:event-node,包括的标签:a、span、label、strong、input、select、button、img、textarea、h1、h2、h3、h4、i等,例如:

<a href="javascript:void(0);" event-node="testevent" event-args="name=testname&value=testvalue"></a>

事件监听方法:load、click、mouseenter、mouseleave

CSS编码规范

命名规范

1.尽量使用英文命名原则,严格禁止拼音、数字
2.尽量不加缩写,除非已约定的缩写方式,如:nav
3.id和class全部小写,采用该版块的英文单词或组合命名,以中杠“-”分割,如:nav-tabs(导航标签/nav+tabs)

书写规范

必须使用Tab进行代码缩进,建议设置Tab为4个空格的宽度,而不是4个空格;
属性与“{}”之间,属性名和值之间必须有空格,属性必须换行,“{}”必须换行显示(即使只有一个属性),如:

.nav-tabs {
    border-bottom: 1px solid #ddd;
}

属性值后面必须加分号“;”,即使只有一个;
RGB颜色值有字母的必须采用小写,可缩写的尽量使用缩写,如:#ffcccc缩写为#fcc;
针对特殊浏览器的属性,应写在标准属性之前,左侧对齐,如:

-webkit-border-radius: 3px;
-moz-border-radius: 3px;
border-radius: 3px;

按照元素模型由外及内,由整体到细节的书写顺序,属性值大致分为五组:

  • 位置:position,top,right,bottom,left,float
  • 盒模型属性:display,margin,padding,width,height
  • 边框与背景:border,background
  • 段落与文本:line-height,text-indent,font,color,text-decoration,text-align…
  • 其他属性:overflow,cursor,visibility,…

标准的元素属性值顺序如:

.carousel-control {
    position: absolute;
    top: 40%;
    left: 15px;
    float: left;
    display: inline-block;
    margin-top: 20px;
    padding: 5px;
    width: 40px;
    height: 40px;
    border: 3px solid #fff;
    -webkit-border-radius: 23px;
    -moz-border-radius: 23px;
    border-radius: 23px;
    background: #222;
    line-height: 30px;
    font-size: 60px;
    font-weight: 100;
    color: #fff;
    text-align: center;
    opacity: 0.5;
    filter: alpha(opacity=50);
}

class和id连写必须换行,如:

.btn-primary:hover,
.btn-primary:focus,
.btn-primary:active,
.btn-primary.active,
.btn-primary.disabled {
    color: #fff;
}

请勿使用冗余低效的CSS写法,如:

ul li a span {
}

注释规范

文件头部注释

文件头部注释主要用来阐述此文件的名称,版本,作者。按照以下形式书写:

/*!
 * family.css
 * Version 2013092614
 * Designed by @yuwenhui <yuwenhui1986@163.com>
 */

其中版本号的格式为:yyyyMMDDHH

行内注释

行内注释主要说明本段代码所在的模块,如:

/*家谱主页*/