《极客与团队(软件工程师的团队生存秘籍)》总结

宗旨:

本书旨在帮助程序员改进理解他人、与人沟通,以及与人合作的能力,进而使其在编写软件的过程中变得更有效率。本书的核心原则就是【谦虚、尊重和信任(Humility,Respect,Trust,HRT)】【高效的团队才是通向成功的关键所在】,你要是能理解并做到这些,后边的内容基本都可以不用看了(最后一章,用户那一章节除外)。

潜在读者:

主要是给那些想要更上一层楼以及编写出色软件的程序员看的,因此你应该具备一下两个基本素质:

  1. 需要和团队里的其他程序员合作。
  2. 喜欢软件工程,并且觉得它应该是一件很有成就感,很好玩的事情。

警告:本书不是技术手册

虽然有一些书能将某些特定问题用纯数学的形式展现出来,而通常这种问题都存在完美的流程化的答案,但本书并不属于这种类型。

编写软件是集体项目,人的因素对结果的影响不亚于技术因素。本书主要关注的是软件开发中与人有关的部分,而人是很复杂的。【人基本上就是由一大堆间歇性bug组成的】。

第一章:天才程序员的传说

首先,无论是篮球还是足球或是其他团队运动,我们都知道一些耳熟能详的巨星,在软件开发领域同样如此,我们都知道莱纳斯(Linux之父),比尔 · 盖茨,乔布斯,我们崇拜他们,希望自己也能变成他们这样的人。

但是事实上,你可能并不是什么天才,虽然你可能很聪明,但你知道世界上有多少天才么?而且天才也会犯错。你想证明你是个天才,然后闭关数月或数年来完成你的某个灵感,打算一鸣惊人。不愿意提前发布是怕创意被偷走,怕别人找出你程序中的错误。

其实这样是弊大于利的,你增加了自己失败的风险,而且浪费了自己成长的可能性。

首先你怎么知道你的路是对的?你很可能在一开始就犯下了很基本的错误, 你很有可能是在重复造轮子

另外你怎么知道你闭关出来之后,你的软件没有过时?人们可能已经不需要它了。

另外就是要考虑:公车因子(一个项目里,需要有多少人被公车撞到才能令其完全瘫痪),越早的分享,越能降低公车因子,毕竟你也不知明天会发生什么意外吧。

要记住一个原则:确保失败尽早发生,尽快发生,经常发生

想想你是如何使用编译器的,你在写代码的时候不可能一口气写完几千行代码才去编译吧?肯定要不断的编译,不断的反馈,不断的改正。这种快速反馈不仅在代码层面,对整个项目来说也是必不可少的。需求总是在往意向不到的方向变化。你闭关了几个月或者几年,这个需求早已跟不上时代了。

所以一句话来说:软件开发是集体项目。你需要合作,告诉别人你的想法,让别人帮你分担劳力,向别人学习,进而打造一支出色的团队。那何为出色的团队呢?答案是很难,但是主要的原则就是:谦虚、尊重和信任。

谦虚:没有人是宇宙的中心。谁也不是万能的,谁都会犯错,你必须不断的提高自己。

尊重:你必须真正的关心你的同事。他们都是活生生的人,他们的能力和成绩都需要得到肯定。

信任:要相信别人的能力和判断力,在适当的时候懂得放权。

最后,你的用户同样需要考虑。这三条同样适用于你对用户的态度,你能从中获得的好处是不言而喻的。

不要低估社交的力量。社交不是勾心斗角,或是操纵别人,它是通过建立人与人之间的关系来把事情做成功,而且这种关系延续的时间肯定比项目本身更长。

HRT的实践:说起来貌似很简单,但是实践起来却不容易。

  • 放下自负。
  • 学会批评和接受批评。
    确保你认识到对某人的创造性工作提出建设性批评和人身攻击之间的区别。如果你真的尊重一个人,那你就会自发的选择有技巧有帮助的措辞。
  • 快速失败;学习;迭代失败是可以接受的。
    广泛的认识是如果没有经历过失败的话,说明你的创新还不够,或者你承担的风险还太小。失败是更上一层楼的绝佳机会。只要产品大致可用,就立刻把它按照原样公开给大众,然后总结失败和成功,尽快的从中学习和迭代。(这一条有待商榷,产品性质各不相同。)
  • 为学习预留时间
  • 学习保持耐心
  • 对影响保持开放的态度你越是容易受影响,你就越能影响别人;你越是示弱,你就越强壮
    以上两句话有点拗口,其实是说,你越是能倾听别人的话语,你就越是能反过来影响别人。你越是承认自己的错误或者无知,你就越是能受到别人的尊重和信任。所以,有时候该诚实的说:“我不知道。”

还有一个可能对大家有用的东西,那就是一份出色的事后检讨应该怎么写:

  • 简要
  • 事件的时间线,从出发到调查,再到最终的结果
  • 事件发生的主因
  • 影响和损失评估
  • 立即修正问题的步骤
  • 防止事件再次发生的步骤
  • 得到的教训

第二章:培养出色的团队文化

团队和团队之间的文化差异是非常大的,它反映了多种多样的价值观和对不同事物的重视程度。

什么是团队文化?

它就像是一块含有酵母的面团:酵母(创始人)能将菌群培养物植入生面团(团队新人),从而变出一块好吃的面包(团队)。如果团队本身具有很强的风格,它就能压过新人带来的任何“坏习惯”。如果团队文化不够强势,团队就会被新人带来的风气影响。由于未知的文化往往伴随着未知的结果,因此一个团队最好拥有自己的熟悉的团队文化。(当然,新人带来的“好习惯“要接受)

团队文化不仅仅是成员们编写代码的方式或者成员之间的相处之道,它还包含了所有人认可的经验、价值观、目标。

为什么要关心团队文化?

简单来说,关心团队文化的原因就在于如果不努力营造它,那么团队最终会因为某个特别强势的人的出现而被注入他个人的文化基因。

大多数工程师都认为建设团队文化是负责人的事,这种想法再离谱不过了。其实每位成员都是团队文化的一部分,都要为定义、维护和保护它做出贡献。每当有新人加入时,他并不是只从团队负责人那里了解团队文化,而是从一起工作的每个团队成员身上学习。

团队文化有意思的地方就在于如果你清楚的定义好它,它是会进行自我选择的。就是指,你的团队文化具有侵略性、欺凌性,那你的团队最终剩下的人都是这种人。如果你的团队文化是温和的,优雅的,那与其格格不入的人也会被自动淘汰掉。

在企业里,团队的自我选择是通过招聘来实现的。如果你在招聘的时候不重视团队文化的契合度,结果招了一个不合适的人,那最后无论是让他融入团队还是请他走人都要耗费你大量的精力,不过结果如何,其代价都非常的高昂。

文化和人

如果你想要优秀的工程师为自己的团队工作,首要的就是雇佣出色的工程师。因为大多数优秀的工程师都喜欢加入那些拥有优秀工程师的团队。

建设性批评是任何工程师团队成长发展的基石,毕竟诤友难得。

优秀团队文化中的沟通模式

工程师通常更喜欢和可理喻的、有逻辑性的人呆在一起。

沟通的指导原则之一就是:在同步沟通的时候( 比如开会),人越少越好,而在异步沟通的时候(比如E-mail),涉及的听众越多越好。更重要的一点是,你必须确保项目文档里的信息要尽可能地让所有人都看到

高层面同步

在最高层面上,整个团队必须目标一致。

任务宗旨——不,我是认真的

撰写任务宗旨就是要准确的定义产品的方向和范围,在撰写的过程中,不断地思考,不断地讨论,让大家在产品的走向上求同存异,否则到时候一定会有各种扯皮并因此拖延进度。

当然任务宗旨也不是一成不变的,如果大环境或者商业计划发生巨变,我们也应该重新评估原来的任务宗旨是否还有价值。任务宗旨应该与时俱进,及时反映公司或者产品的变化。

开会要有效率

前边已经说过了,开会的时候,人越少越好,内容越精简越好。这里有五条开会小贴士可能适合你:

  • 只邀请一定要参加的人
  • 开会前要决定好议程,而且要事先通知所有人
  • 达成目的后应提早散会
  • 注意别跑题
  • 尽量把会议安排在休息时间前后

设计文档

不要一上来就开始写代码,其后果往往惨不忍睹。先准备好设计文档,一般由一个人负责,两到三个人撰写,审核的人可能更多一点。它不但勾勒出整个项目的前景,也直白的告诉整个团队你想做什么以及打算怎么做。这时候你还没开始写代码,比较能接受意见和建议,也更容易帮助你改进产品和优化实现,顺便还能合理的安排工作量。

当然,开始写代码之后它也不是一成不变的。因此千万不要走入另一个极端“唯设计文档论”。

代码注释

注释应该尽量解释为什么代码要这么写,而不是去解释代码做了什么。

强制统一代码风格

每个提交都必须经过代码审查

真正的测试和发布流程

第三章:大海航行靠船长

就算你曾经对天发誓自己永远不会去当“经理”,可是到了职业生涯的某一个阶段,你一定会有意无意的担当一些领导职务。对于影响软件开发的方向来说,理解工程领导学的方方面面是至关重要的技术。

若想要项目有所进展,就必须要有人站出来领导大家,不管是不是有正式任命。如果你有目标有野心,这个人说不定就是你。

主管才是新的经理

传统型经理关心的是怎么完成任务,而主管只关心完成了什么任务……(并且相信团队能自己想出解决问题的办法)。

唯一要担心的就是……好吧,所有的事情

致使大多数工程师都不想当经理的首要原因就是写代码的时间变少了,在忙了一天的“管理”之后,你会觉得,“我今天好像什么事也没干”。不过你要知道这是拓展自己的一个方式。就算你的程序写得非常好,你能写的代码量仍然是有限的。想象一下,如果你能领导一支由优秀工程师组成的团队,那代码的产量能达到多少!

仆人式领导

就是人民的公仆嘛。

反模式

  • 雇佣听话的人你应该努力去雇佣那些比你聪明、可以替代你的人
  • 无视表现不佳的人
    团队里的其他人都清楚地知道谁是短板,因为大家必须拖着他走,无视表现不佳的人还会妨碍优秀新人的加入,导致牛人离开。所以尽量帮助他,类似帮助腿脚不便的人学着再走路,规定一个期限,设定一个小目标,循序渐进。实在不行只能放弃他。
  • 无视人际关系
  • 和谁都是朋友
    你可以在成为强悍的领袖的同时保住和大家的友谊。我们发现和大家一起吃午饭是拉近距离的好办法,还不会让大家不舒服——这样你就有机会在正式的工作环境之外聊一些随性的东西。
  • 降低招聘标准
    史提夫·乔布斯曾经说过:“顶尖的人会雇佣和自己一样优秀的人才,而差一点的人只雇得到更差的人。”参考前边提到的,招聘优秀的工程师。
    找到对的那个人的成本(不管是付给面试官的钱,还是花在广告上的钱,还是寻求推荐的费用)比起招到一个不应该招的员工的代价来绝对是微不足道的。
  • 把团队当小孩子
    把团队当小孩子,就能清楚地传达你不信任他们这样一个信息——通常你怎么待人,他们就会怎么处事。所以如果你把他们当成孩子或者犯人,那么别吃惊,他们的行为真的会像孩子和犯人一样。

领袖的处世之道

  • 放下自负
    虽然前边说过这个,但是这里特别强调一点,是成为领导之后的放下自负,那就是为自己的错误道歉,且是真心实意的道歉。
  • 做一个禅师
    领导的人越多,保持淡定和冷静就越是重要,因为众人都会(不管是不是有意识地)看着你。|禅式管理的另一个秘诀:提问。
    工程师来问你建议通常不是要你去解决他的问题,而是要你帮助他解决问题,所以最简单的方法应该是问问题。
  • 成为催化剂
    团队主管最经常要做的事情之一就是引导大家达成共识。很多时候认识正确的人比知道正确答案要有价值得多。
    催化团队的另一种方法是给予他们安全感,这样他们就愿意接受更高的风险。
  • 当一个导师
    你只要具备三个条件就基本可以胜任了:熟悉团队的流程和系统,向他人解释事物的能力,以及估计被指导的人到底需要多少帮助的能力。
  • 设置明确的目标
    最好的方法就是撰写一份任务宗旨
  • 坦诚
    这并不是说我们认为你在向自己的团队撒谎,这里提到它的原因是有时候不可避免地会出现一些不能告诉团队的事情,或者是你必须要告诉他们一些他们不愿意听到的事情
  • 记录快乐程度
    多关心一下团队在工作之外的情况是很有必要的。此外还有一个重要的部分就是要关心他们的职业生涯。
    记录快乐程度不但是关注(队员的)职业生涯,更是让你的队员有机会得以成长,让他们的工作可以得到认可,同时还能给整个过程多添几分乐趣。
  • 其他建议和窍门
    不必事事躬亲,但也不能当甩手掌柜。
    寻找接班人。
    知道什么时候要做恶人。
    保护团队不受混乱干扰。
    帮团队遮风挡雨。
    告诉团队他们干得很好。

人是植物

工程师也像是植物一样:有些需要更多的光照,有些则需要更多的水分,还有一些则需要更多的肥料)。而你身为领导的任务就是要弄清楚每个人的需求,然后满足他们。

内部激励和外部激励

激励分为两种,外部激励指的是来自外部的力量(比如金钱上的报酬),而内部激励则是来自内在。丹·平克在他的畅销书《驱动力 》里说道,最能让人开心、充满活力的并不是来自外部的激励(比如砸一堆钱给他),而是设法从内心激励他们。

只要能给予以下三样东西就可以达到内部激励的目的:自主、精通、目标

自主:工程师拥有自主权的意思就是他可以独立工作,不需要别人盯着才能干活。

精通:从本质上来讲,所谓的精通就是说你要让工程师有机会学习新技能,在现有的基础上继续磨炼提高。只有给工程师提供大量的机会让他们去学习和掌握技艺,才能保持锐利、高效实用。

目标:说到底,要是一个人不知道为什么工作的话,不管有多大的自主权,不管精通多少技艺都是没有意义的。因此你需要给他一个工作的目标。很多工程师虽然做的产品是非常了不起的,但是这些产品对公司、客户,甚至世界所产生的正面影响和他们都没有关系。其实就算产品的影响力没有那么大,你也可以让他们知道自己的努力是有价值的,以此来激励团队。

第四章:对付害群之马

软件开发里最难得就是跟人打交道,之前的部分我们都是在审视自我,我们学会了谦虚、尊重和信任。那么剩下的少部分,我们要开始审视他人。

什么是“害群”

“害群之马”是很大的帽子,一下子就把“我们”(也就是好人)和“他们”(捣乱的坏蛋)对立了起来。其实完全可以换一个思路来看这个问题。在带领团队的时候,不要把自己想成是一帮精英,众志成城地要把所有的烂人都轰走,而是要培养一种拒绝容忍负面行为的文化氛围,这才是正确的态度。要剔走的是行为本身,而不是人,单纯地区分好人和坏人是很幼稚的想法。规定好哪些是不可容忍的行为,然后予以惩戒,才是更有建设性的务实态度。 简单起见,我们暂时继续采用“害群之马”这种修辞手法来指代那种行为不端的人,但是在日常对话里千万不要轻易使用这个词!

保护团队

  • 写一份明明白白的任务宗旨
    这样可以随时保持专注,知道哪些是目标,哪些不是。
  • E-mail 讨论要有礼仪
    保留归档,要求新人研读,防范那些“嘈杂的少数人”。
  • 所有历史都要有记录
    这不单指代码历史,还有设计决策、重要的bug修复,以及过去犯下的错误。
  • 有效地进行协作
    利用版本控制,代码改动要尽可能的小,方便进行审查,扩大“公车因子”,避免出现领地感 。
  • 修复bug,测试,发布软件要有清晰的政策和流程
  • 降低新人加入时的壁垒
  • 依赖基于共识决策,在无法达成共识的时候也要准备好化解矛盾的方法。

发现威胁

团队的注意力和专注力是最容易受到威胁的。

很少会有人故意干坏事(也就是存心捣乱的那种)。而大多数人在行为出格的时候,要么是没有意识到自己过分了,要么就是根本不在乎别人的感受。无知和冷漠其实比蓄意更严重。绝大多数出格的行为都可以归结为缺乏基本的HRT。

  • 不尊重别人的时间
  • 自负
    这里“自负”可能不是最恰当的词,我们想要表达的是那种无法接受多数人决议,无法倾听和尊重其他观点,以及不愿作出妥协的人。
    这里关键的地方不在于谁对谁错,而是能否和而不同,以及争论是否有继续的必要。一定要提醒自己注意这些问题,有时候你必须作出决定,舍弃一些东西,继续向前。
  • 过分求索
  • 幼稚或是莫名其妙的交流
  • 偏执妄想
  • 完美主义

对抗有害行为

  • 转移完美主义者的注意力
  • 别去搭理那些挑衅的家伙
  • 别太感情用事
    时刻保持冷静,知道哪些人是值得回应的,哪些人是可以无视的
  • 抓住重点
  • 对付挑衅要不卑不亢
  • 知道什么时候应该放弃
  • 关注长远
    在这里要再次明确强调:为了短期利益而打破规矩不值得——特别是对于那些不懂得尊重HRT重要性的家伙来说,再大的天才也没用。

最后再强调一次:不要把用愚蠢可以解释的行为归结为恶意的。

第五章:操纵组织的艺术

大多数软件工程师都服务于效率低下的官僚机构(公司),因此要采取一些非常的手段才能达到目的。有些人称之为办公室政治,你也可以把它叫作社会工程。 而我们给它起的名字是组织操纵。

以上是作者的定义,而这种情况其实是越大的公司越明显,所以大多数人只需要了解一下其中的技巧就可以。

理想的情况:在完美经理手下干活

之前说过的仆人式领导,如果你的经理是这种人,那你真的要庆幸了。这时候建议你:

  1. 在完成自工作的前提下,要求更多的责任把。
  2. 勇于冒险,不怕失败。
  3. 表现得像是成年人一样。
  4. 对于不确定的事情提出疑问

现实的情况:当环境成为成功路上的绊脚石

  • 在糟糕的经理手下当值
    什么是糟糕的,这标准可太多了,但是你肯定可以判断的出。
  • 办公室政治高手
    一般这种人很会利用同僚或下属,踩着他们往上爬。他会抓住各种机会推卸责任,甚至比抢别人功劳还迅速。通常这种人都是两面三刀,见人说人话,见鬼说鬼话,就是希望给你留个好印象。只要相处一段时间,这种人就藏不住狐狸尾巴了。
  • 糟糕的组织
    随着公司的成长壮大,往往会滋生出各种官僚政治和流程,以便管理利润、降低风险、提高预期,以及支撑公司庞大的自重。
    最简单的一个现实就是,大多数公司都不是以工程师为核心的,因此软件工程师一般都是工具人,会有什么的待遇,可想而知。

操纵你的组织

公司也是由规则组成的,有些可以变通,有些还可以打破。

我们都生活在现实里,所以往往遇到各种各样的束缚。这里就是教你如何更好的利用规则做到事半功倍。

  • 请求原谅比请求许可要容易得多
    就算你准备好要请求谅解了,也一定要作出明智的选择——每次你申诉什么事情,或是要反驳公司里的其他人时,你都在消耗自己的政治资本。
    如果你决定选择“请求谅解”,那么最好在公司里找到一些同事和朋友来支持你——特别是那些风险比较高的想法。
  • 路是人走出来的
    在公司里,另一个寻求改变的办法是拉拢群众。
    公司也和人一样,江山易改,本性难移。如果你想要消灭一个坏习惯,最好是用一个好习惯去代替它。
  • 学习向上管理
    你要尽可能确保你的经理以及团队之外的人不但知道你在干嘛,还要知道你干得很棒。
    在做承诺的时候要谨慎,而干工作的时候要尽最大努力。
  • “进取性”和“防御性”的工作
    进取性的工作通常是指用户看得见的新功能——在外人眼里这些都是很炫、很令人兴奋的东西,或是能展现产品优势的地方(比如,界面改进、速度提升,或是互操作性的增强等)。
    防御性的工作主要是着重产品长期的健康状况(比如,代码重构、特性重写、修改数据库模式、数据迁移,或是改进紧急监控等)。这些防御工作能让产品更稳定可靠,可维护性更强。
    不管技术债务有多少,团队也永远不应该花超过三分之一甚至一半的时间和精力去做防御性的工作,否则就等于政治自杀。因为在外人看来,你的项目就好像停滞了一样。
  • 运气和互惠的经济学
    如果你只是逐字逐句地完成任务,除了自己的工作对其他事情都漠不关心的话,是很难看到什么机会的。每家公司都有这种存在于桌子底下的互惠经济。在帮助别人的过程中你也能学到一点新东西,而且助人为乐除了牺牲一点时间和精力外,你能有什么损失呢?
    互惠经济最有意思的地方就在于,哪怕离职了,你的账户也不会被清空——你还是可以在需要的时候找他们帮忙。[朋友有来有去,而敌人却是有增无减的]
  • 晋升到一个安全的位置上
    在公司里的位置越高(不管是作为负责具体工作的人还是担任领导职务),你就越能掌控自己在公司里的命运。
    万一发生什么坏事,你的位置越高,全身而退的希望就越大。
  • 和有能量的人交朋友
    1. 影子人物。这里指的是那些看起来不重要,但却在公司里有权有势的人
    2. 联络人。指认识公司里方方面面的人物的人,就算他们不是直接认识某个人,也能设法帮你找对人。有时候要办成一件事,只要托对人就行了。
    3. 老臣子。虽然不一定位高权重,但是他们知道很多别人不知道的事情,而且凭借资历也能发挥很大的影响力。
    4. 助理。事实上高层的助理在公司里也有极大的权利和影响力。
  • 如何通过E-mail向忙碌的管理层求助
    越短的邮件就越有机会得到回复。
  • B计划:走为上
    努力让自己变得优秀,这样就完全有能力掌握自己的未来,随时走人也不怕。
    只做正确的事,随时准备被炒。

第六章:用户也是人(重点阅读!)

首先是要有一组聪明的程序员,然后遵循谦虚、信任和尊重的原则,为团队培养一种强大的文化氛围。以为团队服务为宗旨来实施领导,鼓励他们合作,引导他们作出正确的决定。根据需要给予足够的水分、阳光、指引,还有来自内心的激励。保护他们远离负面影响——比如威胁到团队文化和生产力的各种破坏性行为(或者环境)。把温度调到28℃,持续烘焙六个月,你就能生产出优秀的软件了。

以上是对之前全部内容的总结,是不是很简单?

别忘了,之前提到过的,不要忘记你的用户。没有用户,你的软件一文不值。

管理大众的印象

这里主要是说营销的重要性,你绝不能忽略营销,你一定要和它打好交道。积极地和营销一起合作是完全可行的,只要你掌握窍门就行了,不需要搞什么乱七八糟的花样。

  • 注意第一印象
    千万不要低估了为产品精心设计的初体验,对情感所产生的影响。
    说得再具体一点,即用户在软件启动后30 秒能体验到了什么?光有一个技术上的答案(“她会看到一个选择菜单,还有一个登录框”)是不够的,还要有一个情感上的答案。新用户在一分钟之内的感受是怎么样的?是感到有用呢,还是觉得很迷惑?你要怎么做才能改善这种体验?
  • 承诺的时候要谨言,做产品的时候要超出预期
  • 尊重业界分析师
    消极抵抗整个大环境(不管它有多么讨厌)是没有意义的。
  • 软件好不好用?
    这里是一个铁的事实:除非你开发的是软件工具,否则工程师并非你的用户。因此,身为一个工程师,你绝对不是评估软件可用性的好人选。对你来说再平常不过的界面,却可能让你家隔壁不懂技术的邻居抓狂不已。
    Google有一句非常著名的格言:关注用户,其他的东西自会随之而来。
  • 选择你的用户
    其实就是要找准自己的目标群体,知道自己所做软件的目标。
  • 考虑入门的门槛
    使用产品的第一分钟是至关重要的。
    如果你想要知道自己的产品入门门槛高不高,不妨做一点简单的试验。随便找一些人(懂技术的和不懂技术的都要)来用你的软件,观察一下他们在头一两分钟内的反应,保证有惊喜。
  • 衡量使用数量,而不是用户数量
  • 速度很重要
    其实就是流畅度,不论是网速的流畅,还是软件使用过程中的流畅。
    改善延迟是提升使用数量、让用户满意的最佳途径之一。正如 Google的创始人喜欢说的:“速度也是特性。”
  • 不要大而全
    与其拙劣地解决所有问题,还不如解决大多数用户都真正会遇到的问题,然后做到最好。少即是多。
  • 别偷懒
    写出方便用户的软件对程序员来说往往是很麻烦的事情。但是你关注的应该是用户,而不是自己写代码方便,再麻烦你也得忍着。
    懒惰的典型反面教材就是展示太多的选择给用户。太多选项只会让人无所适从。
  • 隐藏复杂性
    优雅的设计应该保持简洁,化困难为可能。即便你的软件在处理复杂的逻辑,它也应该让人觉得流畅简洁(我们关注的是用户的情绪)。这就是所谓的“隐藏复杂性”。
    将复杂的问题分解开来,然后隐藏住复杂性,或者至少也要设法让软件看起来很简单。参考Apple的产品设计,或者谷歌的搜索界面,在那个文本框背后的技术含量是惊人的,但是所有的复杂性都被隐藏了起来。
    Apple和Google的界面都神奇得像魔法一般 。这是非常了不起的目标,是所有软件工程师追求的标杆,这基本上就代表了软件的易用性。

管理和用户之间的关系

其实很多用户都想和你的公司或者团队建立起某种联系。满意的用户会想要知道你的软件的演化的情况,愤怒的用户则需要一个地方发泄不满。程序员犯的最大的一个错误就是把做完的软件丢在一边,不再去听取用户反馈。

你不一定要同意他们说的东西,但是你一定要听。

随着时间的推移,软件会收获越来越多的用户;同时,随着产品的“改进”,它也会变得越来越复杂。 问题在于,当用户数量上升时,他们的平均技术水平会递减,因为有越来越多的普通大众变成了你的用户。再加上不断增加的复杂度,用户失望的程度会直线上升。

  • 不要有优越感
    一个常见的误解就是用户都是笨蛋,这加深了我们和用户直接交流的恐惧。
  • 保持耐心
    大多数用户不具备足够的专业术语把问题表达清楚,光是学习、理解他们想说什么就需要很多年的经验。
  • 营造信任和愉悦的氛围
    在和用户交流的时候还有两点需要格外注意:信任和愉悦。
    哪怕只是偶尔让客户小失望一下,随着时间累积,最后他们还是会觉得受到了极大蔑视。
    信任是最神圣的资源,必须悉心呵护、步步为营。任何举动都要三思而行。决策的时候,眼光要长远,不要只注重眼前利益。
    同样,愉悦是另一种能大大改善用户关系的感情。它能提升温暖、无法言语的感觉,让团队更加人性化。这里要提到几点:
    1. 首先是别太把自己当回事。参考google,每年愚人节都会宣布一些奇怪的产品
    2. 尽量给用户带来一些惊叹奇妙的惊喜。一些小小的幽默也算是惊喜。长远来讲,有技巧地增加一点愉悦和幽默,能让用户体会到你是真地关心他们,在乎和他们的关系。

记住你的用户

  • 营销
    了解用户对你的软件的印象是怎么样的,这决定了他们是否愿意尝试你的产品。
  • 易用性
    如果你的软件不好用,速度不够快,界面不友好,访问不方便的话,用户最后一定会离你而去。
  • 客服
    主动和老用户保持联系能影响软件的演化,决定你是不是能留住他们。

作为程序员,我们每天都有各种分散注意力的事情——代码审查,设计审查,摆弄工具,产品出问题了要去救火,还要给bug分门别类——这让我们有时候会忘了自己为什么要编写软件。不是为了自己,不是为了团队,也不是为了公司,而是为了给用户带来方便。关注用户在想什么,如何评论你的产品,以及长期使用的感受是至关重要的,用户才是你的软件成功的源泉。一分耕耘才有一分收获。

Realm 数据库基础使用和注意事项

Realm is a mobile database: a replacement for SQLite & ORMs

目前 Realm-Java 最新版本是 10.8.0,安卓项目最开始使用的是从 5 开始,然后是 6 到目前的版本 10,从 5 到 10 版本使用上面的变化差异不大。因此只要熟悉 10 版本即可

一、RealmConfiguration

Realm DB 配置参数对于使用 Realm 很重要

val builder = RealmConfiguration.Builder()
builder.modules(ExampleModule())
    .assetFile("example.realm")
    .directory(File(context.dataDir, "xxx"))
    .allowWritesOnUiThread(true)
    .allowQueriesOnUiThread(true)
    .name("example.realm")
    .schemaVersion(10)
val configuration = builder.build()

1.1Modules

Realm 比较重要基础概念 Modules,Modules 用来指定 Realm DB 中的表,对应 java 或者 kotlin 里面就是 class,这个 class 通过继承 RealmObject 来实现,当然也可以通过接口实现,具体可以参看 Android 官方文档

@RealmModule(classes = [TargetTags::class])
class ExampleModule

以上就一个很简单的 Module 指定,这个 DB 包含一张表,表明叫做 TargetTags

public class TargetTags extends RealmObject {
@PrimaryKey
private String objectId;
@Index
private String targetId;
@Index
private String tag;

public String getObjectId() {
return objectId;
}

public void setObjectId(String objectId) {
this.objectId = objectId;
}

public String getTargetId() {
return targetId;
}

public void setTargetId(String targetId) {
this.targetId = targetId;
}

public String getTag() {
return tag;
}

public void setTag(String tag) {
this.tag = tag;
}
}

这个类实现了 RealmObject,对外提供了基础方法,并且使其一个字段声明为 primaryKey。当生成Realm DB 文件时,里面就会含有 TargetTags 的表,其中字段有 objectId、targetId、tag

1.2 schemaVersion

数据库版本号,类似于 Sqlite 的版本号,主要目的是针对字段变更、表的增加或者删除的。
对于字段的变更安卓不像 ios 那么方便,即使版本号增加也还是需要手动迁移,因此需要特别注意。
Realm DB在使用的时候就会对字段进行相应检查,如果检查不通过就会对外抛出异常,并且提示使用者哪里出问题,一般表现为该有的字段没有做对应迁移

1.3 directory

数据库目录,如果不指定的话默认就会在应用的 data 目录下面,最好指定对应目录,方便数据库文件的管理及迁移操作,尤其 Realm 打开 DB 文件时候会生成多个辅助文件,为了方便管理务必指定文件路径

1.4 name

DB 在数据库目录下面的文件名称,为了方便管理也最好指定

1.5 assetFile

可以让 Realm 直接指定读取 asset 目录下面准备好的 realm 文件,其实现原理也是通过拷贝 asset 目录下面的资源文件到指定目录。如果需要做比较特殊的迁移操作,或者说废弃掉当前目录下面的 realm 文件,需要提前自行删除或者移动

1.6 migration

数据库迁移操作,用户实现 RealmMigration 接口,在这个接口的

public void migrate(@NotNull DynamicRealm realm, long oldVersion, long newVersion) {
}

方法中做相应表的迁移,有增加字段,删除字段,修改字段,为某个字段添加额外索引,另外增加表删除表也需要在这里面声明。其本身实现原理是 Realm 另外一种使用方法,这种使用方式效率较低,但是对扩充字段比较方便,有兴趣的可以去查看文档 DynamicRealm 使用

1.7 其它

.allowWritesOnUiThread(true)
.allowQueriesOnUiThread(true)
.compactOnLaunch()

protected RealmConfiguration(File realmPath,
        @Nullable String assetFilePath,
        @Nullable byte[] key,
        long schemaVersion,
        @Nullable RealmMigration migration,
        boolean deleteRealmIfMigrationNeeded,
        OsRealmConfig.Durability durability,
        RealmProxyMediator schemaMediator,
        @Nullable RxObservableFactory rxObservableFactory,
        @Nullable FlowFactory flowFactory,
        @Nullable Realm.Transaction initialDataTransaction,
        boolean readOnly,
        @Nullable CompactOnLaunchCallback compactOnLaunch,
        boolean isRecoveryConfiguration,
        long maxNumberOfActiveVersions,
        boolean allowWritesOnUiThread,
        boolean allowQueriesOnUiThread) {}

realm 使用配置地方有很多,重要的还有能否在主线程写入和读取等等。可以在有需要的时候再去查看

二 Realm 使用

使用 RealmConfiguration 可以创建空的 Realm DB 文件,也可以通过拷贝方式拷贝现成 Realm 到指定目录,然后通过

Realm.getInstance(configuration)

直接就可以打开 Realm 对其中的表做数据库标准操作:增、删、改、查

不同于一般的关系型数据库,面向对象型数据库操作都是以对象为基本单位,尤其是修改某个属性时操作比较特殊,直接操作对象本身,这些操作可以去查看官方安卓文档
Realm 还有其他不少,例如删除数据库、数据库数据变更监听等,官方文档写得更加详细

三、transaction

transaction 中文为事务,数据库常用的操作,是旨在保证插入安全,尤其是数据插入失败时,保证数据库数据安全,Realm 的所有写入都需要放在 transaction 中,并且 transaction 中不能套 transaction

在特殊情况时,如果不确定当前写入操作是否在 transaction 需要进行额外判断

realm.isInTransaction()


因此需要进行比较复杂操作时,最好使用封装好的工具方法进行插入,防止出现事务操作中套事务的方式出现异常问题

四、Realm Thread

MongoDB Realm enables simple and safe multithreaded code when you follow these three rules:
Avoid writes on the UI thread if you write on a background thread:
You can write to a realm from any thread, but there can be only one writer at a time. Consequently, write transactions block each other. A write on the UI thread may result in your app appearing unresponsive while it waits for a write on a background thread to complete. If you are using Realm Sync, avoid writing on the UI thread as Sync writes on a background thread.
Don’t pass live objects, collections, or realms to other threads:
Live objects, collections, and realm instances are thread-confined: that is, they are only valid on the thread on which they were created. Practically speaking, this means you cannot pass live instances to other threads. However, Realm Database offers several mechanisms for sharing objects across threads.
Don’t lock to read:
Realm Database’s Multiversion Concurrency Control (MVCC) architecture eliminates the need to lock for read operations. The values you read will never be corrupted or in a partially-modified state. You can freely read from realms on any thread without the need for locks or mutexes. Unnecessarily locking would be a performance bottleneck since each thread might need to wait its turn before reading.

4.1.Realm 对象目前是不能跨线程

在某个线程创建,如果某个线程退出就需要把 Realm 对象关闭,通过调用

realm.close();

Realm 本身实现了一套比较比较复杂的缓存机制,因此每次调用 Realm.getInstance(configuration) 都首先从缓存里面去查找,如果没有才会创建新的,当然不同线程缓存不同,在主线程操作 Realm 最好保证 Realm 存活不要轻易关闭,非主 UI 线程如果线程不是永久存活,就需要考虑 Realm 关闭,否则一是会出现 Ream 不能完全被关闭一直在占用内存,另外一种情况就是 Realm 缓存数目一直在增加,可能会超过上限造成崩溃。

4.2.Realm 查询得到 RealmObject 不能跨线程

通过 Realm 查询到的 RealmObject 不能跨线程,当然目前 Realm SDK 提供了几种跨线程操作

  • To modify the data on two threads, query for the object on both threads using a primary key.
  • To send a fast, read-only view of an object to other threads, freeze the object.
  • To keep and share many read-only views of the object in your app, copy the object from the realm.
  • To react to changes made on any thread, use notifications.
  • To see changes from other threads in the realm on the current thread, refresh your realm instance (event loop threads refresh automatically).

比较重要的思路,一个是通过 id 查找,另外通过 realm 拷贝方式,还可以通过比较高级方式 freeze 方式得到的查询结果,realm 拷贝对内存和 cpu 消耗会比较大,不适合那些高频操作,freeze 会对 realm DB 文件增长影响有点大,因此根据具体使用场景决定使用方向

五、Refreshing Realms

刷新 realm 算是比较高阶操作了,尤其是牵涉到后台线程插入 DB 的时候,如果希望主线程立马能够取到刚刚插入或者更新的数据,那需要在主线程刷新 realm

realm.refresh();

当然 Realm 系统提供了一套自动刷新的机制,前提是当前线程有 looper,如果没有 looper 直接调用刷新逻辑会抛异常。

不管在后台大批量插入数据,又或者小批量插入数据,如果发现主 UI 界面有概率性部分数据显示异常,就需要考虑刷新 realm 数据库了,当然刷新会消耗系统性能,不能频繁操作。

六、Encrypt a Realm

加密 DB,这种操作防止数据轻易外泄,只需要在 RealmConfiguration 中指定即可,当然已经加密的 DB 不能通过没有指定秘钥的 RealmConfiguration 打开,切记。

七、其它

Realm 自从被 MongoDB 收购后增加了不少数据同步操作,尤其是对写入和读取在异步线程中操作判断越来越严格。不管是写入还是读取都是比较消耗性能的,写入更加耗性能。
不建议把大批量的写入和读取操作放在主 UI 线程,这会造成界面卡顿,可以考虑把重量级写入放在后台,回到主 UI 线程直接刷新数据库,又或者可以通过 realm 提供的回调方式

RealmObjectChangeListener

单纯的 query 对象基本不怎么消耗手机性能, realm 实现了一套 lazy load 方式,但是如果还想要去读取对象里面字段,尤其是比较大批量读取字段的时候,这个时候是非常耗 CPU 时间,有兴趣的可以去查看 realm 官方文档。

总结:Realm 为我们提供了比较便利的面向对象级别的数据库操作,好处是上手简单,基本只要稍微看过文档,知道基本使用方式就可以开发,坏处是,随着开发量越来越多,realm 到后期使用会越来越复杂,会牵涉到线程,插入读取,性能损耗之类的,是一种易上手但很难精的数据库。使用过程中一定要谨慎。

官方安卓 Realm: https://docs.mongodb.com/realm/sdk/android/
MongoDB Realm:https://www.mongodb.com/zh-cn/realm
旧有 Realm:https://docs.mongodb.com/realm-legacy/docs/java/latest/index.html#getting-started