《晶体管》游记

倘若程式过于“聪明”,那么世界会变成什么样呢?

成群结队的机器人饲养着人类?遇到天网派来的杀手?或是具现化到地球删除掉我们的城市河流?

由程式构成的世界云提城–其街道、天气、甚至天空的颜色都由程序控制。出厂的角色有音乐家、画家、设计师、赛车手、裁缝店老板、橄榄球运动员、官员议员还有程序管理者等,云提城是现代科技与艺术的完美结合,许多游客乘坐飞机和船只慕名来到此地。

一天,城市的管理员(罗伊斯 布莱克)发现了城市的另一端 观景城中有一把世界的钥匙–晶体管!他与好友数学天才(艾什肯德尔)发现通过晶体管可以轻松的创造或改变现有的世界 比如在观景城和金色长廊放置一座桥。好奇的他向晶体管内窥觑,仿佛看了另一个世界,他继续研究这个拥有魔力的东西。晶体管是一种单项通信的通道,所有人进来就不能回去。期间,他的另一位好友城市的管理者(格兰特肯德尔)得知此事,邀请他成立了一个组织–来不瑞德。

组织的愿景 是重新改造这个城市,但其他成员却被他们的leader欺骗了。格兰特只想要绝对的权利,他不断的杀死对城市产生影响的异端者,而其中的以外事件便是刺杀歌手红玲!我们的主人公红玲天生的嗓音对民众有着极强的感召力,在这座思想先进的城市中,不同的艺术家用不同的方式表达着不同的思潮,而无一例外的,这些创新行为给当权者带来了不稳定因素。受民众爱戴的歌手红玲在一次演出中 被格兰特用晶体管袭击 而男主人公挺身而出救了红玲。醒来的红玲失去了引以为豪的声音,而男主人公也被整合到晶体管内,迷茫的红玲跟随着晶体管内男主人公的声音踏上了,解开一切谜题,挑战来不瑞德的道路。

1.游戏中 晶体管收集了不同进入者的灵魂 根据不同灵魂生前的性格赋予了晶体管本身不同的函数 即不同的技能
2.不同的函数耗费的内存不同 最大耗费 4内存 最少为1 用户所有配置的函数内存消耗总和应小于等于用户的内存上限
3.函数 不同函数可以自由组合配置
作为主函数 (直接使用技能)
作为辅助函数 (增幅主函数技能)
作为全局函数(被动技能)
turn()函数 类似定时器 每隔一段时间 可以进行一次turn()函数操作进行行为规划 触发turn() 函数期间 用户可以移动 和 释放主动技能
4.限制器 增加游戏难度的设置 设置限制器会每次战斗会给与玩家额外经验值奖励
5.升级 可以获得 函数 全局函数槽 技能增幅槽 内存上限 限制器
6.剧情 游戏中除了根据男女主人公的对话获取信息和剧情 可以通过以下途径获取更多信息
OVC控制台
在游戏中检视 人物点 可以解锁函数(人物)的描述 根据不同任务描述推演剧情
击杀不同数量的机器人 解锁限制器的描述

var 与 let区别

变量提升–带有变量提升的变量会在js创建阶段 先赋值为undefined 在运行阶段 赋值为对应值

let声明的变量 没有变量提升特性 是在声明阶段进行赋值 var声明变量有变量提升

function a(){
  console.log(b);//undefine
  console.log(c);//会报错,错误信息:Uncaught ReferenceError: c is not defined
  var b = 1;
  let c = 2
}
a()

let声明变量作用域在对应代码块{}内 只能在对应代码块内调用 且不能被再次赋值

function a(){
  let b = 1;
  let b = 2;//会报错 错误信息:Uncaught SyntaxError: Identifier 'b' has already
}
a()

var声明在全局 则任何处都可以调用 声明在函数内 则在函数任意处都可以调用 声明在块内 也可以在父级域内调用(全局或某层函数)

function a(){
  for(var i=0;i<3;i++){
    console.log(i);//0,1,2
  }
  for(let j=0;j<3;j++){
    console.log(j);//0,1,2
  }
}
a()

function b(){
  for(var i=0;i<3;i++){
    setTimeout(function(){
      console.log(i);//3,3,3
    },0)
  }
  for(let j=0;j<3;j++){
    console.log(j);//0,1,2
    setTimeout(function(){
    },0)
  }
}
b()

微信支付weixinjsbridge.invoke()和wx.choosewxpay的区别

weixinjsbridge.invoke()出现的版本更早 无需引用jssdk 无需wx.config方法注入 需要参数appId 使用回调 有详细的说明
而wx.choosewxpay出现的版本比较晚 需要jssdk注入 不需要参数appId 使用回调 只有SUCCESS 和 FAIL没有具体的说明
weixinjsbridge.invoke()是微信浏览器的内置方法 其实wx.choosewxpay在引用的微信jssdk文件中 也调用了weixinjsbridge.invoke() 是对weixinjsbridge.invoke() 的再次封装
综上所诉 这是微信前后设计的不同方法的支付 还是weixinjsbridge更方便一些 有具体的失败回调

小程序开发采坑

1 .json文件中不能使用单引号

2.wx:for={{arr}} 模板传入参数如果用 this.data 里的参数
属性值 + 标签中属性值 需分别使用

A. {{paras}} + paras.item模式
B. {{…paras}} +item模式

3.使用Content-type来指定不同格式的请求信息
wx.request()其中 默认为GET请求 对应 header 中 ‘Content-Type’ 为’application/json’
而POST请求 对应 header 为 “application/x-www-form-urlencoded”

4.注意组件map外部父元素尺寸问题 地图demo 中 尺寸写在css中

5.页面加载时 初始化函数 onload 执行时不能 操作 this.data

生命周期说明 this.data加载 在 onload 之后 如果这样定义成功回调函数:

success:function(res){ … }

在 success 函数执行后 onload才结束 之后this.data进行加载 故而不能加载初始化数据

异步加载定义方法:

success:res =>{ … }

其中res为回调返回值

6.小程序数据接口地址必须使用https

7.小程序异步数据绑定

不能直接使用this.data因为异步操作的函数中作用域不包含this变量
也不能直接使用this.key = XXX 会造成只改数据层 不能 修改视图层
var object = this;
将类this与新定义的对象object绑定 在异步函数里 object可以被操作或引用

在需要修改data的地方使用 要修改的数据key值不同 互不影响

object.setData({
key:val
})

8.初始化变量必须使用 var 或 const 否则报错

9.标签属性 逻辑语句 中注意不要有空格或其他字符串 会被解释成string 当做true来运行

如 wx:if=”{{false}} ” 条件后面有空格会被当做true解释

10.支付API变量使用小写字母加下划线组合,微信支付使用驼峰法定义变量
万恶的appId “I” 居然是大写

11.再说授权

步骤一涉及app.js文件
步骤二三四涉及a.js文件 a.wxml和组件页面z.wxml

步骤一 :app.js写入静默授权
前台传入js_code (本次登录凭证)
服务端 用js_code换取openid 如果没有用户注册 有用户直接返回结果
附带用户是否授权信息
存入本地数据localstorage

具体页面a.js
步骤二 : 设置组件浮层
添加z.wxml组件 提示窗 提示用户授权设置授权按钮
浮层实现与否绑定 a.data.auth

步骤三 : 设置加载方法
添加全局方法 在onload中 判断本地数据中是否授权 操作auth属性控制组件浮层是否显示

步骤三: 设置授权方法
点击授权按钮 拿到用户授权结果 如果用户同意授权
启动该全局方法 注意变量传入本页数据 this
在全局方法中操作a页数据this.data.auth和本地数据

12.未发布小程序二维码 扫码后提示未发布
未发布小程序二维码只能在微信开发工具中 通过 二维码变异进入对应页面进行调试

13.微信原生组件 与 需要用户主动点击按钮的 带有授权用途的 功能冲突

如微信弹窗 底部菜单等 不能触发 带有open-type属性的动作 不能绑定对应触发方法

14.配置模板消息url 微信文档没有说明 验证token之后应该作何反应
微信公众号 是返回token
小程序返回 返回$_GET[‘echostr’]

15.小程序 IOS、安卓 解释页面跳转函数不一样
IOS 关闭页面后 不执行该页面的其他跳转函数 即卸载了该页面
安卓 关闭页面后 执行剩余其他函数

造成的结果是IOS在关闭上一层页面和跳转到新页面 只能选一项 而且跳转到新页面没有切换页面的动画效果

16 IOS渲染bug
hidden属性为真的元素 在IOS渲染没有被完全隐藏 会出现在左上角 留出一条缝
尽量使用wx:if避免此问题

17 canvas画图 – 开发工具错误
设置文字 文字大小 开发工具识别居然小数点 真机不认识 造成文字很小

18 服务短消息 – 开发工具错误
通过开发工具 支付扫码获取的prepay_id不能发送服务短消息 会提示41028 form_id不正确

前端js直接导出生成csv文件

本文链接: http://www.zxlucky.com/wordpress/archives/296

作者:陶路

场景:

有些场景下,前端需要筛选数据内容 编辑数据内容 之后生成表格文件 或者不想泄露给服务端数据资料,该方法无需通过服务端 可以直接在浏览器生成csv文件

csv文件格式定义方法:csv文件可以用excel打开 ,每行间隔用 “\n” 隔开,每列间隔用“,”隔开

标签代码:

<a id="out2csv"  href="#" >导出csv</a>

js代码:

out2csv.click(function(){
    str = 'id,name,tel\n1,taolu,02423187000\n2,taodalu,02423186000'
    out2csv.attr('href', 'data:text/txt;charset=utf-8,\ufeff'+str);
    out2csv.attr('download', 'out2csv.csv');
    out2csv.click();
})
可能遇到的问题:

在mac上可能无法打开 可以用商店里的其他软件打开csv文件

另外 导出的csv文件 excel因为没有任何数据描述信息 可能存在将电话号 订单编号等长数字数据强制转换成科学计数法的 数字类型 解决这个问题 参考其他应用里 采用了在数据前 加“`”字符的方法 导出的数据会携带该字符 做excel导入的时候需要特殊处理

继续阅读前端js直接导出生成csv文件

阿里云备案、域名迁入、转移备案信息

2017年新版阿里云备案

自2017年1月1日其 原阿里云备案账号 不能继续注册
但可以登录绑定阿里云账号
地址
阿里云下产品每个Ecs实例可以申请五个备案账号 即可以绑定备案五个一级域名
阿里云虚机只能绑定一个一级域名
备案需要 空间+域名进行绑定
每个阿里云账号对应的阿里云备案账号的主体只能是一个
但阿里云账号实名认证/企业认证与阿里云备案账号的备案主体可以不同 呸 绕嘴不说 明显逻辑bug 推测原因是没考虑历史数据 无法变更导致结果
阿里云账号无需实名认证 只需要 对应的 阿里云备案账号 主体资料通过审核即可
总之所以你必须重新注册一个阿里云账号
备案流程:
1.注册阿里云账号:
需要的注册信息:
用户名(可以是中文)
密码
手机(可以绑定了其他阿里云个人/企业账号)
可以不绑定邮箱
2.登录对应阿里云备案账号 开始备案
需要填写资料:
企业所在地区选择 省市县
主办单位
主板单位证件类型(一般选工商营业执照)
证件号
域名
备案服务号(从阿里云-备案管理处 申请 必须购买Ecs或虚机产品)
主板单位区域 名称 住所 通讯地址 投资人或管理公司
负责人姓名
负责人证件类型
负责人证件号
联系方式(手机号)
验证码
联系方式2(必须)
应急联系电话
电子邮件
网站名称
网站域名
可以同时备案多可域名到同一空间上
3.上传网站备案核验资料
阿里云APP流程
下载承诺书(用于授权阿里帮助备案提交管局) 盖章
授权 阿里云计算有限公司 和 本省省份名称
拍摄负责人本人照片
负责人身份证正反面照片(身份证为真实实物 需要APP扫描)
主体法人身份证正反面照片
PC上传主要区别在于 授权书 换成了自己下载填写核验单 盖章上传
注意:PC上可以上传 正反面照片 和 公司营业执照 如果在APP上没提供直接上传身份证照片的功能 只能使用自动识别身份证的方法 因此在PC上上传身份证 较方便
4.阿里云代办备案 确认备案
提交完资料后 阿里云在1天内会询问你是否为本人或授权填写 及 开网站目的 确认后 阿里云会将资料提交对应的省份通信管局
之后只能等待管局审批

2016年整理企业备案及域名迁入

1.域名
2.若为企业类型 需要工商营业执照
3.服务器类型
4.服务器地址
5.服务器用户名 密码
6.负责人信息 包括 办公室电话(必须) 手机号码 证件号码 电子邮件地址证件正反照 需要向备案负责人发送短信验证
7.企业信息 包括 所在区域 主办单位名称 主办单位证件住所 主办单位通讯地址 投资人或主管单位 营业执照 证件正反照
如果是备案迁移
8.额外需要备案密码
95187-3阿里云备案客服
域名迁移
新购买域名 需60天后可以转移 部分服务商更改联系方式 续费页要等60天
1.需要注册商提供转移密码 修改原注册商处域名解析地址 为 新域名解析地址 打开域名锁
入户注册商操作 阿里云为例
2.需要入户阿里云账户 主要阿里云账户 账户需要实名认证
企业认证 负责人信息 包括 办公室电话(必须) 手机号码 证件号码 电子邮件地址证件正反照 需要向备案负责人发送短信验证
企业信息 包括 所在区域 主办单位名称 主办单位证件住所 邮编 主办单位通讯地址 投资人或主管单位 营业执照 证件正反照
个人认证 手机号码 身份证号 邮编 证件正反照 短信验证
3.域名信息模板填写 并 实名认证 参照步骤2 最好以公司名称+负责人填写 (公司法人忙)
4.域名入户 填写域名和转移密码 https://wanwang.aliyun.com/domain/transfers
过户需要交首年年费 .com域名45元首年
5.需要验证邮箱 邮箱为原域名所有者邮箱
邮箱不正确,请按照以下步骤完成:
如已开启域名注册信息保护(whois保护),请先在原注册商处取消保护;
在原域名注册商处修改域名所有者邮箱;
如果您修改了邮箱,此处显示邮箱还未更新,请耐心等待,更新时间取决于原注册商域名信息发布时间,一般1-8天完成更新,具体更新时间可咨询原注册商。
6.系统对转移密码进行验证 转入状态为 授权转入 等待 转入成功大约需要5-7天的处理时间,取决于原注册商确认转出的速度 阿里云不到半天
若在转移域名前修改原注册商的 个人信息 如用于验证是否本人同意的邮箱 电话等信息 个别注册服务商会在一定周期内禁止转移域名
另 新注册的域名不能转移 周期由服务商定 国际惯例为2个月
7.成功后解析 使用

转移备案信息

同一网络空间服务商的空间更换 无需修改备案信息
新版阿里云备案账户即阿里云账号
2017年更新后,新版阿里云账号注册 只需要一个手机号即可注册 用户名可以是汉字、邮箱、手机号、支付宝账号、微博账号、钉钉账号
注册成功后的阿里云账号 无需认证企业主体或个人认证 无需绑定邮箱即可开启新版阿里云备案 点击右上角 备案即可
★旧版备案账户导入新版备案账户:
阿里云账户(新版阿里云备案)如果没有主体 可以导入旧版阿里云备案主体及备案记录
条件:提供旧版账号邮箱及旧版备案账户(备案账户拥有者)的手机验证
步骤:
创建阿里云账户或使用已有阿里云账户
点击右上角备案
点击进入备案系统
点击右上角”导入备案信息”
填写信息
注意:原阿里云备案记录转入新版阿里云备案 新版备案账户不能有主体
★将备案信息转移至其他账户:
进行备案信息转移的两个账号都必须是新版备案账号(阿里云账号)
条件:被转移账号的手机验证 及 新账号用户名及密码
步骤:
登录被转移信息的阿里云账户
点击右上角备案
点击进入备案系统
点击右上角“转移备案信息”
填写信息
★如何将两个阿里云账户备案信息互相调换
注册新阿里云账户做中间账户

常用meta标签 小结

1.PC宽度 自动100%

<meta name="viewport" content="width=device-width, initial-scale=1" />
width=device-width兼容winphone ie浏览器

2.文档类型/编码

<meta http-equiv="Content-Type" content="text/html; charset=gb2312" />
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />

3.对搜索引擎友好

<meta name="keywords" content="别搜我" />
<meta name="description" content="告诉你别搜我,对,就你" />
<meta name="revised" content="Frank, 2016/3/1" />
//定义网页版本
<meta name="robots" content="index,follow" />

4.重定向 相当于js跳转

<script>window.location.href="";</script>
<meta http-equiv="refresh" content="5;url=" http:="" zxlucky.com"="">

5.强制全屏/竖屏 移动端

<meta name="full-screen" content="yes" />
<meta name="browsermode" content="application" />

6.兼容ie浏览器的版本

<meta http-equiv="X-UA-Compatible" content="IE=8" />
<meta http-equiv="X-UA-Compatible" content="IE=Edge" />

7.360等国内浏览器 号称双核浏览器的渲染兼容模式

<meta name="renderer" content="webkit" />
//默认webkit内核
<meta name="renderer" content="ie-comp" />
//默认IE兼容模式
<meta name="renderer" content="ie-stand" />
//默认IE标准模式

8.其他

<meta name="author" content="" />
网站作者 <strong>iphone webapp常用meta标签小结</strong> 1.宽度
<meta name="”viewport”" content="”width=device-width;" initial-scale="1.0;" maximum-scale="1.0;" user-scalable="0;”" />
移动端safari浏览器宽度适配 尤其针对打开输入键盘后 iphone自动改变屏幕宽度有显著疗效

2.添加到主屏幕后,全屏显示。

<meta name="apple-touch-fullscreen" content="yes" />

3.去掉工具栏

<meta name="apple-mobile-web-app-capable" content="yes" />

这meta的作用就是删除默认的苹果工具栏和菜单栏。content有两个值”yes”和”no”,当我们需要显示工具栏和菜单栏时,这个行meta就不用加了,默认就是显示。

4.添加到主屏后的标题(iOS 6开始)

<meta name="apple-mobile-web-app-title" content="标题" />

5.设置状态栏的背景颜色

<meta name="apple-mobile-web-app-title" content="yes" />

6苹果手机浏览器safari会自动识别页面中的数字和邮件 并进行拨号和发邮件操作

<meta content=”telephone=no” name=”format-detection” />
<meta content=”email=no” name=”format-detection” />

腾讯地图 多个覆盖物设置点击事件

今天一个项目用到地图,因为是基于微信公众号网页,因此选用腾讯地图。
优点:腾讯地图申请权限简单 只要有QQ号 填写基本的开发者认证资料即可 而且可以立刻获得秘钥 无需人工审核 经过验证,在实际项目中,即使不使用秘钥也可以开发项目,但腾讯官方的声明是不保证之后永久有效。
项目是网页开发模式,JDK选择 WEB开发 JavascriptAPI。前面获取点击位置,添加覆盖物等操作均没有问题,不过在进行为覆盖物添加点击事件的时候出现了问题。为一个覆盖物添加点击事件,OK没有问题,但在进行多个覆盖物的点击事件添加后。会出现每个覆盖物的事件都被覆盖(重写)成最后一个事件,也就是所有覆盖物出发的事件都是一个而且是最后一个添加的点击事件。

data是商铺数组信息
一开始,读了腾讯官方文档 写了如下代码,这是用单一插点 模拟的多个插点。


for(var i in data){
shop = data[i];
x = shop.shop_position.split(',')[0];
y = shop.shop_position.split(',')[1];
//--插点
marker = new qq.maps.Marker({
position: new qq.maps.LatLng(x,y),
animation: qq.maps.MarkerAnimation.BOUNCE,
map: map,
draggable: true,
icon: new qq.maps.MarkerImage("img/point.png"),
visible: true,
});
//--插点点击事件
qq.maps.event.addListener(marker[i], 'click', function() {
//--事件函数
});
//--跳转到最后一个搜索结果的位置
map.panTo(new qq.maps.LatLng(x,y));
}

结果是插点成功,但绑定的事件不成功,事件被最后一个覆盖。

之后看到有多个覆盖物的插点文档:http://lbs.qq.com/javascript_v2/case-run.html#sample-event-closure


for(var i in data){
shop = data[i];
x = shop.shop_position.split(',')[0];
y = shop.shop_position.split(',')[1];
//--标记插点
marker[i] = new qq.maps.Marker({
position: new qq.maps.LatLng(x,y),
animation: qq.maps.MarkerAnimation.BOUNCE,
map: map,
draggable: true,
icon: new qq.maps.MarkerImage("img/point.png"),
visible: true,
});

var infoWin = new qq.maps.InfoWindow({
map: map
});

latlngs[i] = new qq.maps.LatLng(x,y);

//添加到提示窗
info[i] = new qq.maps.InfoWindow({
map: map
});
//--插点点击事件1
qq.maps.event.addListener(marker[i], 'click', function() {
info[i].open();
info[i].setContent('

'+shop.shop_name+'

');
info[i].setPosition(new qq.maps.LatLng(x,y));
});

//--插点点击事件2
qq.maps.event.addListener(marker[i], 'click', function() {
//--点击事件内容
});
//--跳转到最后一个搜索结果的位置
map.panTo(new qq.maps.LatLng(x,y));

}

这次注意上面已经使用数组进行创建覆盖物和点击事件,插点点击事件1是弹出腾讯默认给出的提示框(蛮不错),插点点击事件2是项目需要的逻辑。又一次失败!但是再一次接近了真相!当时已经卡了一个多小时了,郁闷!

经过本大神冥思苦相,果然真相只有一个!说多了都是废话,还是上码吧,同志!

for(var i in data){
shop[i] = data[i];
x = shop[i].shop_position.split(',')[0];
y = shop[i].shop_position.split(',')[1];

var infoWin = new qq.maps.InfoWindow({
map: map
});

latlngs[i] = new qq.maps.LatLng(x,y);

//--创建marker以及点击事件
(function(i){

var marker = new qq.maps.Marker({
position: new qq.maps.LatLng(x,y),
animation: qq.maps.MarkerAnimation.BOUNCE,
icon: new qq.maps.MarkerImage("img/point.png"),
map: map
});
qq.maps.event.addListener(marker, 'click', function() {
//--业务逻辑 顺便加了一个腾讯自带提示框 加了一张图片

});

})(i);

//--跳转到最后一个搜索结果的位置
map.panTo(new qq.maps.LatLng(x,y));

}

最后发现:腾讯地图中覆盖物点击事件触发时,调用的时JS在内存中储存的变量信息,我本以为既然官方文档中的marker既然是重复使用的,那么在创建覆盖物点击事件的时候,函数的内容已经固定(函数内部函数已经赋值),由此看到腾讯地图的开发者的习性!
而我的习惯是在元素创建时为其创建点击方法,在元素创建的时候,函数内容已经确定。比较起来,如果数据量大,创建元素众多的情况下,腾讯开发者的思路会减少浏览器储存的信息,占用的空间等于函数+变量(覆盖物信息数组);而我的方法是创建一个函数数组(函数内含有数组信息)。

腾讯文档地址:http://lbs.qq.com/javascript_v2/demo.html

[转载]陈皓:什么是工程师文化?

作者: 陈皓 来源: 酷壳 原文链接 

四年前,我在 QCon 上演讲了一个《建一支强大的小团队》(整理后的 PPT 分享于这里)提到了工程师文化,今天,我想在这里再写一篇关于工程师文化的文章,一方面是因为我又有了一些想法和体会,另一方面,因为我也正走在创业的道路,毫无疑问,要建一个有浓重的工程师文化的团队或公司,所以有必要把自己的相关想法开有成白底黑字的“字据”,以供打自己的脸——“要是未来没有做到,这篇文章就打我未来的脸” “这篇文章太幼稚了,未来的我会打我现在的脸”,我希望是前者。
  Again,这篇文章不是招人的贴子,因为我觉得,招聘第一重要的事,不是发招聘广告或是找猎头挖人,而是先得让自己变成一个能配得上真正工程师的公司,然后再谈吸引人的事。
  为什么要工程师文化
  看看最近二十年来社会的发展,计算机和互联网已经渗透到了这个社会的每一个角落,各式各样的计算机技术成为了整个世界发展的强大引擎,各式各样的创新,无论是业务创新还是技术创新,都是依托于技术的快速演进,技术成了解放生产力提高社会运作的效率的中坚力量。以美帝国主义为首的技术创新公司着着实实的改变着这个世界和人类的生活和生产习惯。
  今天,每个从事计算机行业的技术人员都应该感到幸运,因为,我们不但选对了行业,也出生在了正确的时代,可能感受到前所未有的刺激和变化,相比起我们的父辈,我们的人生,能经历这样的时代,实在是一种幸运。所以,选对了职业并出生在了正确的年代的我们,只是需要思考的一个问题就是,我是否呆在了正确的地方?
  在我看来,这个世界上有三种商业公司,
运营或销售驱动型的公司。这类的公司以运营和营销见长,技术对于他们来说,更多的只是为了支持大规模的营销活动,以及成本上的控制,所以,基本上来说不需要技术创新。这种公司最大的问题就是缺乏安全感。
产品驱动型的公司。这类公司以产品见长,通过创造能提升用户生活体验的产品见长,技术对于他们来说,除了支持大规模的在线用户之外,他们会更多的去寻找那些为了增强用户体验,提高整个业务流程效率的技术创新。比如:UI 的交互方面的,整个业务流程方面的。这种公司最大的问题,就是容易被别人模仿和抄袭。
技术驱动型的公司。这类的公司相信技术能改变世界,他们更多的是用强大的工程技术来创造有颠覆性的东西,更多的是用各种自动化的技术取代人类。比如:近代的蒸汽机技术取代了大量的人工,数字技术取代了大量信息传递的人工,现代,这类公司还希望通过人工智能来取代愚蠢的人类来做决定。这种公司最大的问题就是可能做出叫好不叫座的东西。
  这三种公司都可能成功,也都有问题,但是,无一例外,他们都需要强大的技术支撑,只不过,他们把技术所放在的位置不一样。
  无论你有多么的看不起技术人员,你都无法否认,你今天的生活相当的依赖这帮工程师,没有他们,你恐怕都不知道怎么生活了。邓爷爷几十年前就说过——“科学技术是第一生产力” ,无论什么样的科学技术的理论要落地都会依赖于工程技术有多先进。
  所以,在今天,作为一个 IT 或互联网公司,“工程师文化”不是一个问题,而是一个常识!
  工程师文化的特征
  我下面罗列的这些特征来源于,Google 的《重新定义公司》,我在 Amazon 的经历,37Signals 的《Rework》,Quora 上的 What Makes Good Engineering Culture? Slideshare 上的 What Makes Good Engineering Culture,以及我最近这半年来的一些实践。
  对我来说,我可以简单的把这么多的工程师文化的总结成两大类:“自由” 和 “效率”。
  本来还应该有个“创新”,但我个人认为,创新的前提是——在自由的环境下对提高效率的痴迷,就一定会发生创新。
  创新不是凭空出现新的东西,其实,观察一下人类的发展史,不难发现,几乎所有的创新基本上跳出原来的思维模式用新的思维模式对原有问题的效率进行质的提升。比如:通信、交通、医疗、教育、生活……几乎全都是在优化效率。
  所以,如果你的精神不自由,你很难跳出老的思维模式,你用老的思维模式你一定不会想到新的方法和方式,如果不是对效率的提升,这个创新可能会不接地气。
  自由
  首先,工程师文化意味的创新文化,工程师都是有创新冲动的人,因为手里有创造技能的人通常都想创造点什么。而创新的源泉水来源于精神的解放,精神自由才会引发各式各样的奇思怪想,才会有常人觉得不可能的疯狂想法和想像力,而这些想法和想像力导致了创新。
  精神上的自由具体表现在:
自我驱动。自己管理自己是最好的管理。最失败的管理就是家长和保姆式的管理。兴趣出发的工作才可能迸发出真正的动力。
灵活的工作时间和地点。工程师们更多的是脑力工作,而不是体力工作,工作上时间和地点的自由安排可以让工程师们的脑力工作更有效。Remote 是一个很不错的工作方式,开源社区基本上都是这钟方式。和 Remote 有关的话题可参看这本书《Remote》
信息平等。这意味着,全体员工得到的是原始信息,而不是被管理者们层层加工消化后的信息,大的包括战略、方向、目标、财务,小的包括文档、代码、和知识的共享等。同样,也表现在意见表达上,任何人都有可能表达自己的意见和建议的平等机会,这样才会激发出更多的思路和思辩,从而有不同的更好的思路出现。而不是,大家都看到了问题,而没有人敢说。在 Google 除了代码全员共享,还有 Thanks God, It’s Friday 的文化,每周五,高管们会出来,任员工提各种尖锐的问题。在 Amazon,代码和文档基本上全员开放,包括财务报表也对员工开放,另外,除了所有的 NB 的 Principle SDE 隔三岔五都会有一个 Principle Talk,有很多 Talk 相当令人开脑洞,还有 Amazon 内部的 Up the River 文化,每年会选出一批公司最聪明最有想法的人集思会,讨公司下一步的和战略,并可以把相应的 KPI 直接按给 Senior VP。
不害怕错误。处理错误的正确的姿势是分析总结教训,而不是惩罚故障人。前者让人改善进步,后者让人萎缩不前。最大的错误就是不敢犯错,最大的问题就是不敢直面问题。
宽松的审批系统甚至没有审批系统。审批通常暗示着三件事,1)对人的不完全信任,2)繁琐的流程,3)思维上的束服。这些都是创新和想像力的天敌。一个公司的监管、审批、流程越重,这个公司的活力也就越差。
20% 的自由时间。这是 Google 公司提出来的,员工有 20% 自由的时间做自己想做的项目,Gmail 就是这么出来的。
  效率
  工程师天生是追求效率的。有人说认为程序员花大量的时间做自动化的工具,还不如人肉的效率高,比如,写自动化的脚本花 5 个小时,而重复做这件事 200 次只花 3 个小时。有这样的理解的人根本不懂工程。
  一方面,这个工具可以共享重用,更多的人可以从中受益,而不是微观上的比较。更重要的是,这是一种文化,一种提高效率的文化,他会鼓励更多的这样的事情发生。如果你因为一个程序员花大量的时间开发自动化的工具,而认为这个程序员没有效率,对之批评甚至惩罚的话,那么你就扼杀了提高效率的文化(关于效率,大家可以看看我的另一篇文章《关于加班和效率》,你会真正了解什么是效率)
  人类之所以比别的动物聪明就是会使用和发明工具,而古语也有云:“工欲善其事,必先利其器”,看看美军的装备你就知道战争工具的好坏有多重要了,一个公司的强大之处在执行力,而执行力的强大之处在于你有什么样的支持工具。这些,已经不是工程师文化,而是人类发展的文化。
  针对于工程师文化来说,尤其是软件工程,提升工程效率的具体表现如下:
简化。简化不是简陋,简单的东西通常意味着用户更好理解,也意味着更容易的维护和运维。就像阿里推行的“小而美”,就像乔布期推崇的“没有产品手册简单易用的产品”,就像 Amazon 推行的 Working Backwards 里说的那样,一个新的产品或功能,产品经理需要写三个文档:媒体公关文、用户手册、常见问题,三个文档不准备超过两页 A4 纸,且不准用任何图片说明。
残酷无情的推行自动化。编写程序的最本质的东西就是自动化,看看人类发展史上自动化了多少东西。对于自动化来说,不仅仅只是消除人肉的重复劳动,更重要的是,很多事情人完全干不过机器,比如加一台机器,程序在秒级就可以完成,人是永远不可能达到这样的速度的。自动化需要大力开发提高生产力的工具,比如:持续集成,持续部署,自动化运维,基础自动化运维,甚至自动化的运营工具。(Amazon 的软件工程中对自动化和简代相当迷恋)
避免无效率的组织架构和无效率的管理。这体现在这些方面:1)扁平化的组织架构,2)努力用自动化工具取代支持型的工作,3)不超过 10 个人的全栈小团队,4)不按人员的技能分工而是按其负责的产品或功能分工(关于分工,请参看《让我们来谈谈分工》),5)通过产品的目标或信条 Tenets 来减少沟通和决策过程(Amazon 里的每个部门,每个团队,每个产品都有自己的 Tenets,这个 Tenets 标明了要什么不要什么,比如:AWS 的几个信条:运维是最高优级的——这意味着只要是会让运维变得复杂的需求都可能会工程团队被拒掉,Throughput & Laentcy 不能更差——这意味着,功能要为性能让路,因为性能变差了,用户就要买更多的资源)
正确的组件抽象。抽象是简化的一部份,一方面,抽象意味着重用和通用,另一方面抽象意味着强大的扩展性,以适配各种可能性。最重要的是,抽象意味着技术能力的输出,无论是内部的其它团队还外部的团队。比如:Google 的 MapReduce/BigTable,FaceBook 的 Thrift,还有 Amazon 内部的 WebService 框架 Coral Service、处理日志监控的 Timber,以及全线 AWS 产品都用到的 Amazon Lock Framework(一个分布式锁框架)……
开发高质量的产品。因为高质量的代码,不但可以容易的修改和维护,还可以因为少处理线上故障,从而有更多的时间去为未来做更多创造性的工作。这意味着需要有非常严谨的 Design Review,Code Review,以及测试,关于 Code Review,可以参看这篇文章《从 Code Review 谈如何做技术》,关于严谨的测试,可以参看这篇文章《如果做性能测试》
不断的提高标准以及招聘最好的人。取法其上,得乎其中,取法其中,得乎其下,取法其下,法不得也。如果一个公司或一个团队想变得越来越好,越来越强大的话,就必需要不断的提高自己的工作标准,提高工作标准意味着要不断地招聘最好的人。在 Amazon 和 Google 的招聘官中都有一个叫 Bar Rasier 的人,这个人就是为了提高招聘标准而设立的。
创建一个持续改善的文化。一个好的组织,一个好的团队,是需要不断反思前进的,这需要全体员工一起来的。微观层面上,在项目做完后需要有一个总结会分析项目中的得失,在故障出现后,需要有故障分析会,反思得失,在 Amazon,严重的故障,需要写一个 COE(Correction of Errors)的文档,其中有一节叫“Ask 5 Whys”,让你自己问自己至少 5 个为什么。在宏观层面,一个公司每年都应该做一定的工作数据分析或是员工调查,比如,是否招聘到了不错的人、工作的投入产出比,员工在哪些地方花时间了,等等,然后不断的用技术手段来改善。(Amazon 每年的工程师员工调查表是我活那么大见过的最细最细的调查表了, 问题除了对公司、经理、文化的,还有从,日常工作、开发环境、持结集成,测试自动化、产品质量、软件架构、软件维护、线上问题处理、年度计划、数据仓库建设、通用工具投票……这个员工调查直接导致公司的对工程的投资方向)
  工程师文化如何落地
  如果你要让任何文化在公司内得到执行,你有下面几个手段可以选择:
通过政治手段:你需要把三个地方——招聘、绩效考核 & 升职。比如,你要落地工程师文化中的简化和自动化,那你你在招聘的时候,你需要把懂简化和喜欢自动化的人招进来,然后在绩效考核和升职的地方设置上一条硬性指标——你今年简化了什么?自动化了什么?如果没有,对不起不但不能升职,绩效可能还不达标。
通过经济手段:让不做这事的成本 > 要做这个的成本。然后,正常的人类都会选择成本低的方案。比如,如果你要推行 Design/Code Review/UT 以提高质量,你就把 QA 和 OPS 团队全挪到一边去,让 Dev 团队自己测试,自己负责,而 QA 和 OPS 团队只是帮你做工具罢了,而测试和运维的事全是你 DEV 的 Ownership,出了故障也是 dev 自己负责,于是,他们就会发现,不做 code review 和 ut 的成本远远大于做 code review/ut 的成本,他们就会去做的。
  最后,工程师文化要落地,还有几个小条件,
第一,团队要小,Ownership 很重要,Eat Your Own Dog Food。 没有人帮你擦屁股,自己的屎自己吃,没有痛苦,不会产生想进步的动力。
第二,热爱学习和尝试,学习尝试新的技术,开拓眼界,学习尝试新的思维方式,否则,呆在原地,原有的思维方式只会让你在原地打转转。
第三,老板更多的相信技术而不是管理。相信技术会用技术来解决问题,相信管理,那就只会有制度、流程和价值观来解决问题。
  其它
  说了这么多,时代还在发展,不过,这是我这么多年经历或看到的工程师文化的东西了。最后吐几个槽——
  对于 996 和加班这个事,对于工程师来说从来都不是问题,在解决技术问题或是创造的时候,工程师是个很自觉的群体,基本不需要有别人驱动,工程师是最乐意 Work Hard 的人了。我相信几乎所有走上编程这个职业的人来说,基本上都是兴趣所至,觉得编程很有趣,但却被各个公司 996 搞得对编程毫无兴趣。为什么,你们这些公司要向中国的教育学习呢?人家本来对这事有比较高的兴趣的,但就是要通过考试/KPI/996 这些东西把人家的兴趣一点一点的磨灭掉,把人变成机器、奴隶、牲口,让人对学习和工作产生了厌倦和讨厌,会是你们这些管理者们所希望的?是不是只有把人变得不思进取了,你们才会管理?就像《软件开发中的两种管理方式》中说的第一种人一样?
  另外,我不知道,为什么我一说这些东西,就会有很多人(包括程序员自己)来和我说我是个理想主义者,这些已经不是什么理想了,已被很多成功的公司用了很多很多年了。只是你没有见到过罢了。还有的人说,因为中国的国情不同。这更让我费解了。这让我想到了当年大清朝派了一堆人出国考察后回来后,说外国的那套共和的东西不符合中国国情,最终也在历史的潮流中被淹没掉了。另外,什么叫“中国的国情不同”?中国有全世界数一数二的互联网用户,也有全世界数一数二的市场,不再是以前那个一穷二白的年代了,中国的国情到底有哪些不同呢?
  我不知道各位工程师是为什么活的?但我觉得,我们选择了一个刺激的职业,也赶上了这个行业大发展的时代,我们不妨扪心自问一下,你是否愿意让自己的能力、青春和热情就这样被磨灭了?
  (全文完)