人生中唯一的2020

今年是工作的第十年,北漂的第十年,时间过得很快,很感慨!~

还清晰的记得2010年刚毕业,刚到北京时的窘境,押一付三交完房租兜里所剩无几,离发第一个月工资还有时日,吃饭需要按计划供应的日子,还记得那时候每天下班去吃8块钱一份的西红柿鸡蛋盖饭!~~~

没有一个好的学历,没有人指导,一路靠自己摸索过来真的很不容易。

这十年来,「持续学习」成了工作与生活的主旋律!~

今年开启了工作十年来的第四段职业生涯,拿到了陌陌、滴滴、美团的offer,最终选择了base比滴滴低2k的美团~

现在想想刚毕业时的薪资是真的低,工作十年,薪资也从刚毕业的基础上上涨了十倍,这也是对这十年努力的认可吧,当然也必须感谢大环境以及认识到平台的溢价!~~

过去的2020

关于今年的一些记录点

读书

  • 读完《永恒的终结》《金字塔原理》《长安的荔枝》《长夜难明》
  • 工作之余断断续续读《人性的枷锁》,目前差不多读完40%,的确是一本好书,据说每个人都能在书中找到80%的自己

今年读书偏少,明年需加油。

学习

之前喜欢通过RSS订阅以及啃技术书的方式学习技术,感觉今年换成了读公众号的推送,以及在极客时间上学习了,今年学习了这么几个专栏

  • 《Elasticsearch核心技术与实战》学完后半部分
  • 学完《浏览器工作原理与实践》,很有收获
  • 《设计模式之美》、《web协议详解与抓包实战》差不多有只看了一半,希望在新的一年里能够学完

关于技术学习这块,感觉今年缺少有计划的学习。

工作

  • 上半年:疫情的原因,很辛苦,加速了职业培训业务线的线上化进程,还记得初四就开始远程评审需求了!~从负责保洁业务线调整到负责职业培训业务线,收获还是很大的,从git上的第一行代码,到CDN、服务器、Node,小程序等从0搭建起一条全新业务线的技术体系。线上卖课、线上学习、线上考试、线上颁发结业证书,从0见证了职业培训业务线的线上化过程。
  • 下半年:到了一个很成熟的业务线,带一个小组负责一个180+入口页面的营销管理系统。涨了很多眼界,见识到了成熟的业务建设、技术建设是什么样子,很有收获。

其它

  • 利用入职新公司的间歇,花了一周多时间上线网站digdigdig.vip
  • 小孩儿上幼儿园的第一天与入职新公司为同一天
  • 第三份工作2018.3.15入职,2020.9.11离职,都是一个特殊的日子
  • 英语学习今年捡了起来,从「AnkiDroid」换到了「一点英语」APP,每天的目标是花5分钟学习or复习8个单词,从5月份到年末,总共坚持240天,中间只间断了一天,学习了2127个单词,超出上一年订的小目标「每天新记3个单词,一年1000」
  • 今年博客写了22篇,相对于年初的目标「尽量保持每月一篇」算是超额完成
  • 虽然大专文凭也找到了工作,但是内心还是挺在意的,有总比没有好,所以今年报了个专升本

未来的2021

下面是flag时间

  • 读书,优先排《原则》、《高效能人士的七个习惯》、《学会提问》、《用图表说话》
  • 2020读一半的书读完,极客时间上学一半的课程学完
  • 始终觉得英语学习在缺少英语的使用场景下很难速成,再次订个小目标,每天新记3个单词,一年1000,现在这个阶段完全没有了考试的压力,寄希望于用时间来打败空间。
  • 在新公司发现勤于总结于思考是一个好习惯,希望今年坚持每周都记录一下每周的总结和思考,不论长短
  • 新开的公众号一直疏于打理,希望在今年能够做到两周一更的样子,结合每周的总结于思考,尽量往每周一更靠齐

留言

欢迎交流想法。留言会通过 GitHub Issues 保存,首次使用需要登录 GitHub。

关于「康威定律」

之前在各个文档里面看到一个词,叫做「康威定律」

康威定律 (康威法则 , Conway’s Law) 是马尔文·康威1967年提出的:
“设计系统的架构受制于产生这些设计的组织的沟通结构。”
——M. Conway[1]

对维基百科里的这么几个点印象深刻

  • 康威定律源于模块的设计者需要互相之间频繁沟通。而跨部门交流比较难。

    之所以会比较难,是因为相互间没有利益关系,要想跨团队推动一件事情,首要的事情就是要找准「共赢点」

  • 如果团队、部门、子部门等的组织结构没有紧密反映产品的必要组成或产品组成的关系,那么项目将会遇到麻烦。因此,应该确保组织结构兼容于产品架构。

    这一条阐明了除了找准「共赢点」,还应该尽可能的争取更上一级的「组织保障」,跨团队的项目才能够快速顺利的往前推进

  • 时间再多一件事情也不可能做的完美,但总有时间做完一件事情

    在一开始就追求完美,往往会苦不堪言。合理的方式是制订Roadmap,通过一期、二期、三期一步一个脚印的慢慢走向完美。

wikipedia: https://zh.wikipedia.org/wiki/%E5%BA%B7%E5%A8%81%E5%AE%9A%E5%BE%8B

留言

欢迎交流想法。留言会通过 GitHub Issues 保存,首次使用需要登录 GitHub。

关于祖传“屎山”代码的一些思考

目前负责一个持续迭代了好几年的系统,同时还是两种技术栈并存,很多文件都是上千行的代码,完全不具备可阅读性,修改一个变量为了防止修改不完全,只能靠文件搜索找引用依赖,十分炸裂…

对于我这种有点代码洁癖的人来说,简直欲罢不能,时刻都在想着如何能改变“屎山”代码的现状…

重构?

但是很大的问题在于业务逻辑相当复杂,没人知道详细逻辑,而且线上稳定运行,使用频繁,风险很大。

然后还很难回答,重构能给业务带来什么收益?

越想越无力!~

偶然在知乎上看到这样两个问题:

看完下面各种欢乐的回答,感觉跟“屎山”代码和解了!~

这其中有一句话说得好:再差的代码也是经过线上验证过的!

而且,重构的代码从最终结果来看,不一定能赶上重构前的代码,至少没有经过线上各种情况的锤炼。

相信不论哪个系统,当不停迭代几年之后,或多或少都会有这样的问题。

原因很简单:那就是越到后面,迭代的人越需要小心翼翼,只能基于现有的设计与架构去新增业务逻辑,从而代码结构越来越变形,渐渐变成“屎山”代码。

从目前的情况,换个角度来看,实际上还是有很多有价值的东西的,比如

有哪些好的方式可以避免一个长期迭代的系统代码味道慢慢变味儿?

从最开始的系统架构、中间的迭代、最新业务逻辑的增加可以有哪些思路去规避最终的问题?

可以站在业务最终态的角度去思考业务系统的发展逻辑,有没有办法从最开始的架构设计上减轻目前的困境?

总的来说,学会妥协,并且从中发现能让自己有收获的点,一切都坦然了。

留言

欢迎交流想法。留言会通过 GitHub Issues 保存,首次使用需要登录 GitHub。

在一个成熟稳定的团队如何找到技术发力点?

入职新公司已经一个多月了,带着两个同学负责一个营销运营管理系统,这个系统已经建设了五年以上,支撑起一条业务线所有营销活动的营销策略以及相关配置,总共100+的菜单入口…

在这一个多月的时间里,对所负责的业务,以及团队的整体情况有了一个大致的了解。

在这期间,随着对团队业务与技术建设不断的了解深入,越来越觉得在这样一个成熟的团队里很难干出成绩,很难拿到一个比较优质的结果去支撑下一步的晋升。

另外就是,对于团队目前正在做的一些技术建设项目,抱有很多的疑惑以及不解,最根本原因就是觉得这些东西有些遥远,没有解决什么痛点问题,有点为了做而做。

虽然有这些困惑,但是想要在这里继续干下去,就不得不去思考这么一个问题,那就是:在一个业务成熟、稳定的团队如何找到技术发力点?

因为最近几周一直在思考这个问题,正巧也听到TL关于技术建设的一些想法,感觉有了一些思路。

由于之前的工作环境,以及所负责的业务属于冲刺型业务,所以不自主的会优先想到做的东西能解决目前哪些痛点问题?按照解决痛点问题的思路去做技术规划与设计。

但是,这种思路在平稳期的业务场景下是行不通的,因为这个时候业务问题、技术问题都已经解决得七七八八了,按照这个思路去做,会陷入无事可做的状态。

所以,需要转变思路,需要站在更高的维度、更远的将来去思考问题。

比如要为将来搭一个什么平台,建一些什么能力相关的技术事项,虽然短期看无法找到很明显的收益与效果,但是从长期来看意义还是很大的。

按照解决日常问题的思路,可以实现从普通到优秀。

按照后一种思路,可以从优秀达到顶尖。

这也是为什么一些最新的技术、设计思路都是从大厂扩散开的。一个是因为他们的业务挑战摆在那里,另外一个很重要的原因他们还能在这个基础上,站在更高的视野,更远的将来去规划技术的建设与发展。

虽然,走在最前面开辟一条新的道路失败的几率远远大于成功,但是如果不去做,那么就只能在现有业务、技术框架下原地踏步,无法突破。

即使最后没有拿到一个好的结果,这个过程也是很有意义和价值的。

留言

欢迎交流想法。留言会通过 GitHub Issues 保存,首次使用需要登录 GitHub。

《金字塔原理》要点汇总

第1章为 什么要用金字塔结构

  1. 为了交流方便,必须将思想(观点、结论、要点、论点、论据、建议、行动、步骤等)归类分组。
  2. 将分组后的思想,按照不同层次,进行抽象提炼、总结概括,搭建金字塔。
  3. 向读者介绍(传递、阐述、论证)思想最有效的途径,是结论先行,自上而下表达。
  4. 金字塔中的思想,应遵守4个基本原则(见右图)。
  5. 条理清晰的关键,是把你的思想组织成金字塔结构,并在写作前用金字塔原理检查。

关键概念

金字塔原理的4个基本原则.
· 结论先行:每篇文章只有一个中心思想,并放在文章的最前面。
· 以上统下:每一层次上的思想必须是对下一层次思想的总结概括。
· 归类分组:每一组中的思想必须属于同一逻辑范畴。
· 逻辑递进:每一组中的思想必须按照逻辑顺序排列。

提示

金字塔中的思想以3种方式互相关联一一一向上、向下和横向。位于一组思想的上一个层次的思想是对这一组思想的概括,这一组思想则是对其上一层次思想的解释和支持。
文章中的思想必须符合以下规则:

  1. 纵向:文章中任一层次上的思想必须是其下一层次思想的概括。
  2. 横向:每组中的思想必须属于同一逻辑范畴。
  3. 横向:每组中的思想必须按逻辑顺序组织。

提示

组织思想基本上只可能有4种逻辑顺序:
· 演绎顺序:大前提、小前提、结论
· 时间(步骤)顺序:第一、第二、第三
· 结构(空间)顺序:波士顿、纽约、华盛顿
· 程度(重要性)顺序:最重要、次重要,等等

第2章 金字塔内部的结构

  1. 金字塔结构的各个层级上包括各种思想;思想使受众(包括读者、听众、观众或学员)产生疑问。
  2. 在纵向方向上,各分支、各层级上的思想,与读者进行疑问/回答式对话。
  3. 在横向方向上,各种思想以演绎推理或归纳推理方式回答读者的疑问,但两种方式不可同时使用。
  4. 序言的讲故事形式是为了提醒读者,文章将回答的读者最初的疑问。
  5. 序言包括背景、冲突、读者的疑问和作者的回答。冲突因背景而产生,背景和冲突都是读者已知的事实。
  6. 冲突导致读者提出疑问,而文章将回答读者的疑问。

关键概念

金字塔结构中的逻辑关系
· 各种思想纵向相关(疑问/回答式对话)
· 各种思想横向相关(演绎/归纳,MECE原则)
· 金字塔顶端思想回答的疑问,来自读者已知的事实
· 序言引出读者最初的疑问

提示

金字塔中的子结构,能够加快你梳理思想的过程。
· 主题与子主题之间的纵向关系
· 各子主题之间的横向关系
· 序言的叙述方式
这三种子结构(即纵向的疑问/回答式对话、横向的演绎或归纳逻辑、讲故事式的序言结构)能够帮助你找到构建金字塔所需的思想。
了解了纵向关系,你就可以确定某一层次上的思想组必须表达哪种信息(即:必须回答针对上一层次思想提出的疑问)。
了解了横向关系,你就可以判断,你组织在一起的思想是否以符合逻辑的方式表达了这种信息(即:是否是正确的归纳或演绎论述)。
更重要的是,了解读者最早提出的问题,将确保你组织起来的思想与读者有关联(即:文章中的思想有助于回答读者的初始问题)。

第3章 如何构建金字塔

自上而下法

· 确定作者想论述的主题
· 设想读者的疑问
· 给出答案
· 检查背景和冲突是否引发读者提出疑问
· 证实答案
· 填写关键句要点

提示

自上而下法构建金字塔的步骤
· 提出主题思想
· 设想受众的主要疑问
· 写序言:背景-冲突-疑问-回答
· 与受众进行疑问/回答式对话
· 对受众的新疑问,充分进行疑问/回答式对话

自下而上法

· 列出所有作者想表达的要点
· 找出各要点之间的关系
· 得出结论
· 到推出序言

  • 如果读者在阅读的最初30秒内还不能清楚了解你的全部思路,你就应该重写这篇文章。
  • 如果文章较长,用小标题。标题应呈现某种思想、观点或结论,而不只说明要讨论问题的类别,例如,不要用“我们的发现”,“结论”这类的标题。因为对于读者快速了解文章的主要内容和观点没有太大帮助。

关键概念

构建金字塔

  • 确定主题
  • 设想疑问
  • 给出答案
  • 检查背景和冲突是否引发读提出疑问
  • 证实答案
  • 填写关键句要点

初学者注意事项

  1. 一定先搭结构,先尝试自上而下法
  2. 序言先写背景,将背景作为序言的起点
    一旦你知道自己想在序言的主体部分说些什么——情境、冲突、疑问和回答――你就可以根据你想产生的效果,以任何顺序写出这些要素。
  3. 先多花时间思考序言,不要忽略对序言部分的思考
  4. 将历史背景放在序言部分
  5. 序言部分仅涉及读者不会对其真实性提出怀疑的内容
    序言的目的只是告诉读者一些他们已经知道的信息。
  6. 在关键句层次上更宜使用归纳法而非演绎法

第4章 序言的具体写法

  1. 序言的目的是提示读者已知的信息,而不是提供新信息。
  2. 序言通常包括背景、冲突、读者的疑问和作者的答案。
  3. 序言的长短,取决于读者的需要和主题的要求。
  4. 为每个关键句要点写一段引言

关键概念

写序言
· 说明背景(S, Situation)
· 指出冲突(C, Complication)
· 冲突引发读者提出疑问(Q,Question)
· 作者的文章给出答案(A,Answer)

序言基本结构

序言必须采取“背景(S)—冲突(C)— 疑问(Q)— 回答(A)”的结构,但是各个部分的顺序可以有所变化。
· 标准结构:背景 – 冲突 – 答案
· 开门见山式结构: 答案 – 背景 – 冲突
· 突出忧虑式:冲突 – 背景 – 答案
· 突出信心式:疑问 – 背景 – 答案

第5章 演绎推理与归纳推理

  1. 演绎推理是一种论证,其中第二个论点对第一个论点加以评论,第三个论点说明前两个论点同时存在时的含义。
  2. 对演绎推理的概括,就是把最后一个论点作为主体,概括整个推理过程。
  3. 归纳推理是把具有相似性的思想归类分组,根据各要点具有的共同性得出结论。
  4. 在关键句层次,使用归纳推理比演绎推理更方便读者理解。

关键概念

逻辑推理
· 演绎推理,是一系列线性推理过程。
· 归纳推理,是把相似的、具有共同点的思想或相关的行动归类分组。
· 在关键句要点层次,使用归纳推理,比演绎推理更利于读者理解。
记住:当你进行演绎推理时,推理过程的第二个思想必须是对第一个思想的主语或谓语的评述。如果不具备这一点,就不是演绎推理而是归纳推理。

提示

用归纳法时,必须具备以下两项主要技能:
· 正确定义该组思想。找到一个能够表示该组所有思想共同点的名词。
· 识别并剔除改组四中与其他思想不相称(不属同类、不具备共同点的思想)

提示

演绎推理与归纳推理的区别
演绎推理:第二点是对第一点主语或谓语的论述。
归纳推理:同组中的思想具有类似的主语或谓语。

第6章 应用逻辑顺序

  1. 应用逻辑顺序可以确你不会:
    • 把新闻条目当做思想列人
    • 遗漏某一组中重要的思想
  2. 任一组思想的逻辑顺序都呈现了该组思想的分组基础。
    • 时间顺序:通过设想某一流程,得出的思想
    • 结构顺序:通过评论某一结构,得出的思想。
    • 程度顺序:通过按程度或重要性不同分组,得出的思想
  3. 如果你在某一组思想中找不到以上顺序,说明这些思想之间不存在逻辑关系,或者你的思考还不周全。
  4. 为了检查一组思想的逻辑顺序,你可以:
    • 先把每一个句子改写成能说明其实质的短句(即只保留主语、谓语、宾语,删除定语、状语和补,只保留动词、名词,删除形容词、副词)。
    • 再把相匹配或具有共同点的句子合并同类项,组织在一起。
    • 最后选择使用适当的顺序。
  5. 如果思想属于行动性思想(即说明行动、活动、行为、动作、步骤、流程等)
    • 明确说明每一行动产生的最终结果(效果、目标)
    • 把能产生同样最终结果的行动(行为、步骤等)归类分组
    • 确定该组思想的分组基础(类别),并依此排序。
    • 检查是否遗漏了任何步骤。
  6. 如果思想属于描述性思想(即介绍观点、情况、信息等)
    • 把说明类似事务,或具有共同点的思想归类分组。
    • 确定该组思想的分组基础(找出相似之处、共同占)
    • 把所有思想转换成完整的句子,并决定其顺序。
    • 检查是否遗漏了任何步骤。

关键概念

将行动性思想(说明行动、活动、行为、步骤、流程)排序.

  • 明确说明每一行动产生的最终结果。
  • 把能产生相同最终结果的思想合并、归类、分组。
  • 确定该组思想的分组基础,并依此排序。
  • 检查是否遗漏了任何步骤。

关键概念

将描述性思想(介绍观点、论点、论据、情况、信息)排序

  • 把说明类似事务、或具有共同点的思想归类、合并、分组。
  • 确定该组思想的分组基础。
  • 把所有思想转换成完整的句子,并决定其顺序。
  • 检查是否遗漏了任何步骤。

第7章 概括各组思想

  1. 避免使用“缺乏思想”的句子(比如“存在3个问题·一··”等)
  2. 分组应遵守“相互独立不重叠,完全穷尽无遗漏”(MECE)原则。
  3. 行动总是按时间顺序进行,通过说明行动产生的直接结果,概括行动性思想。
  4. 把描述性思想归类分组,是因为该组思想具有共同特生它们都:
    • 针对同一类主语一针对同一类谓语(动作或对象)一包含同一类判断
  5. 将行动性思想分组时,要求:
    • 发掘每一个行动的本质
    • 区分不同的抽象层次(比如,采取一项行动,是必须在另一行动开始之前,还是为了完成另一行动?)
    • 明确说明行动产生的最终结果
    • 直接由行动概括出结果
  6. 将描述性思想分组时,要求:
    • 找出句中结构的共同点
    • 确定包括这些思想的最小范畴
    • 说明共同点隐含的意义

关键概念

概括各组思想

  • 通过说明行动产生的直接结果,概括行动性思想(概括一组行动)。
  • 通过说明各思想具有的共同点、相似性,概括描述性思想(概括一组信息)。

关键概念

寻找思想的共同点

  • 各思想是否针对同一类主题
  • 各思想是否涉及同一类行动
  • 各思想是否针对同一类对象
  • 各思想是否包含同一类观点

关键概念

行动性思想分组

  • 发掘每个行动的实质
  • 区分行动的不同层次
  • 明确说明行动性思想产生的最终结果(效果、目标)
  • 直接由行动概括出结果

关键概念

描述性思想分组

  • 找出主语、谓语、宾语或含义的共同性
  • 确定包括这些思想的最小范畴
  • 说明共同性隐含的意义

第8章 界定问题

  1. 展开“问题”的各要素
    • 切人点/序幕(产生问题的具体领域、方面)
    • 困扰/困惑(它的发生打乱了该领域的稳定)
    • 现状R1(你不喜欢该方面正在产生的结果)
    • 目标R2(你希望在该方面得到的结果)
    • 答案(到目前为止,针对问题已经采取的措施,如果采取了的话)。
  2. 把界定的问题转换成序言
    • 从左往右再往下
    • 读者最后知道的事实就是冲突

关键概念

界定问题

  • 设想问题产生的领域
  • 说明什么事情的发生打乱了该领域的稳定(困扰/困惑)
  • 确定非期望结果(现状,R1)
  • 确定期望结果(目标,R2)
  • 确定是否已经采取了解决问题的行动
  • 确定分析所要回答的疑问
  • 疑问(为了解决问题,必须做什么)

第9章 结构化分析问题

  1. 使用诊断框架,呈现存在问题领域的详细结构,展示一个系统内的各个单位是如何相互影响的。
    • 查找具有因果关系的活动
    • 将产生问题的可能原因进行分类
  2. 收集资料,以证明或排除结构中导致问题产生的部分
  3. 使用逻辑树
    • 产生和检验解决方案
    • 揭示思想一览表的内在关系

关键概念

结构化分析问题

  • 界定问题
  • 使用诊断框架,呈现存在问题领域的详细结构
  • 假设产生问题的可能原因
  • 收集信息,以证明或排除所作假设

第10章 在书面上呈现金字塔

用多级标题、行首缩进、下划线和数字编号的方法,突出显示文章的整体结构

  1. 表现金字塔结构中主要组合之间的过渡
  2. 表现金字塔结构中主要思想组之间的过渡

第11章 在PPT演示文稿中呈现金字塔

  1. 制作文字幻灯片,尽量简明扼要
  2. 制作图表幻灯片,使传达的信息更简单易懂
  3. 使用故事梗概,简要说明整体结构
  4. 排练排练,再排练。

第12章 在字里行间呈现金字塔

  1. 画脑图(在大脑中画图像,或画思维导图)
  2. 把图像复制成文字

留言

欢迎交流想法。留言会通过 GitHub Issues 保存,首次使用需要登录 GitHub。

《长夜难明》

这是第一本大部分内容都是听完的书,看和听总共花费8小时。

首先讲讲读完这本书的第一感觉。

读完这本书最让我记忆最深的是如下两句话

  • “郭红霞是个坚强的人,我也是。”
  • 这一晚,他们喝了很多的酒,说了很少的话。天很黑,他们不知道什么时候才能亮起来。

关于第一句话,读到的时候被那种隐忍的坚强所深深打动,一种说不出来的压抑感。

关于第二句话,在全书的结尾处,虽然对于黑暗,天迟早会亮起来,只是早晚的问题。然而,在等待中却是无比的煎熬,在实际的现实中,一些黑暗可能永远见不到光明,真正的长夜难明!~

之所以会选择看这本书,是因为最近很火的网剧《沉默的真想》改编自此书,该书的作者是紫金陈,之前还有两部剧《无证之罪》《隐秘的角落》也是改编自他的小说。这两部剧是难得的好剧,节奏紧凑,不拖沓。

从《长夜难明》里能够很明显看到《无证之罪》的影子,第一下就会想到会不会是同一个作者。

整本书来说,还是挺值得一看的。

留言

欢迎交流想法。留言会通过 GitHub Issues 保存,首次使用需要登录 GitHub。

如何做好一线团队管理?

之所以想聊聊这个话题,是因为最近面试有一半的面试官都会问这个问题,分享一下我对一线团队管理的粗浅看法。

对上

  • 及时反馈

    这里不仅指老板交代的事项要及时反馈与主动担责,还指遇到项目问题或者需要老板支持的事项需要及时反馈。

  • 适当提要求

    结合上一条,针对具体的事项需要向老板适当的提一些要求。比如,解决这个问题预计需要两天,做这个复杂项目需要两个人员支持,让老板协调资源等等

  • 给出靠谱结论

    结合上一条,提要求的同时,一定要给出靠谱结论。比如需要老板协调两个资源协助项目开发,一定要给出需要什么技术水平的人员,具体支持多久,什么时候可以释放,资源到位后具体是怎么安排的等等。老板交代的事项也同理,一定要给出里程碑计划,比如预计明天到什么里程碑,周几能完全搞定等。

  • 信息及时同步

    信息及时同步的核心目的在于防止信息差带来认知的不一样。比如团队接下来一个季度的计划、想做哪些技术基础设施让老板知晓并给出建议、线上问题及时同步给老板避免更上一级老板问到直属老板时一脸懵逼等。

对下

  • 建立信任,上下一心

    作为一个团队leader,我觉得这一条是最重要的,需要排在首位。只有相互建立了信任,大家才能够上下一心,只有上下一心,在遇到困难时才能够同舟共济,携手向前。
    怎么才能建立信任呢?我觉得核心就两点:一个是体现自己技术方向的专业可信赖,另一个就是发自内心的希望与带领团队向好的方向发展,让团队的每一个成员都能够在团队中成长与提升。

  • 目标清晰,节点跟踪

    每一件任务都需要给出明确的预期目标,并且不能等着最终拿结果,需要对关键节点进行跟踪,避免目标偏差。

  • 氛围打造,开心工作

    大家每天面对的是冰冷的机器,不能让氛围也变得冰冷。一个好的氛围,会使得所有人干活儿更有冲劲,面对困难更少的畏难情绪,带来更高的工作效率。
    怎么打造一个好的氛围呢?我觉得可以从这几个方面入手:

    1. 每周组织好文章新技术资料导读与分享,打造好的技术氛围,同时还可以借助技术的讨论拉近大家的距离
    2. Leader主动承担一些零碎的事项,让团队成员能够安心的工作
    3. 做好格挡。比如一些不合理的需求,质量不高的需要应该在还没到团队成员之前就格挡回去。比如明知团队工作饱和度很高,一些低优需求就应该格挡回去,或者重新做优先级排序,切记不顾团队同学的实际情况做传声筒
  • 梯队建设,人尽其才

    做好团队人才盘点,结合业务,梯队合理,人员合理搭档。根据团队人员不同的技术能力与性格特点,安排合适的事项,人尽其才,提供最大的发挥空间。

对外

  • 个人&团队影响力建设、体现专业、建立信任

    首要的还是建立信任。信任的来源在于专业以及做事靠谱。
    具体怎么做?可以考虑通过技术分享、业务流程分享、无可挑剔的项目设计与排期、重大项目复盘等方式来增加个人&团队的影响力。

  • 定好规则制度

    有完善的规则制度,大家沟通协作起来才会更加的顺畅。
    比如:跟PM定好需求到什么程度才能做需求评审,评审到什么程度才能做排期;跟QA定好需求CASE通过率达到多少才能提测,否则打回;跟UI/UE对齐组件规范,UI走查规则;跟RD定好接口规范,接口规则等。

  • 知己知彼,了解其他组业务、技术、人员情况

    了解其他组的人员那情况,比如人员梯队情况,技术能力情况,这样当各个团队的人组合到一起承接项目时才能知道会产生什么化学反应。

  • 为团队争取更多空间

    有空间大家才会有充足的动力,空间不会从天上掉下来,需要靠争取与挖掘。
    比如:了解到大团队中各个团队都没有做某一项技术基础设施,比如组件库、性能监控等,那么可以主动向老板提,看能不能让自己组把这一块技术基础设施做下来。 - 争取
    比如:发现营销侧营销活动页面很多,而且很多页面有一些相同的规则。那么,可以引导PM,是不是可以通过技术手段解决,比如做一个营销活动页面自动生成与发布的系统。 - 挖掘

最后

最后,一线团队管理一半需要专业能力够强,另一半则是考验一个人除开技术之外的综合能力。有些人也真的从性格上来说天生就不善言辞,不适应做团队管理。所以,最重要的还是认清自己的优劣势,扬长避短,做最适合自己的选择。

附上本文的脑图

最后再啰嗦一句,最近调休在家整理了一下面试过程中一些有实际意义的技术问题,已经发布在了blog上,这些问题都是一些小点,本着让更多的人受益的原则,接下来会每天搬一篇到公众号上来。

留言

欢迎交流想法。留言会通过 GitHub Issues 保存,首次使用需要登录 GitHub。

递归调用栈溢出问题分析与解决

问题模拟

让递归爆栈还是很简单的,例如如下代码就能轻而易举实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// 故意来一次爆栈体验
function stackOverflow(){
stackOverflow()
}
stackOverflow()
// 结果
VM42:3 Uncaught RangeError: Maximum call stack size exceeded
at stackOverflow (<anonymous>:3:5)
at stackOverflow (<anonymous>:3:5)
at stackOverflow (<anonymous>:3:5)
at stackOverflow (<anonymous>:3:5)
at stackOverflow (<anonymous>:3:5)
at stackOverflow (<anonymous>:3:5)
at stackOverflow (<anonymous>:3:5)
at stackOverflow (<anonymous>:3:5)
at stackOverflow (<anonymous>:3:5)
at stackOverflow (<anonymous>:3:5)

我们还能通过如下代码看到浏览器最大的调用栈是多少

1
2
3
4
5
6
7
8
9
10
(function getStackDepth(){
try {
return 1 + getStackDepth()
} catch (e) {
// Call stack overflow
return 1
}
})()
// 结果
12539

数量取决于方法体所占用的空间,占用空间越大,可用栈数量越小。可尝试在方法中增加变量,会发现结果变小。

原因分析

我们都知道,程序运行时有堆内存,还有栈内存。
每一次方法执行,都会把方法压到执行栈中。
栈空间是有大小的,当超过栈空间大小,就会触发Maximum call stack size exceeded异常。

关于调用栈之前的这篇文章中有说明:CodeReview过程中关于JS代码性能的随想整理

解决方案

有了原因分析,解决方案也清晰了,那就是想办法规避掉不停的往调用栈塞方法。

样例准备

准备一个相加的例子(这个例子不具备实际意义,本来想准备个阶乘的例子,发现还没爆栈就超出整形长度了,累加也一样,所以就出现了这么一个看起来有点脑残的样例…)

1
2
3
4
5
6
7
8
// 用递归实现相加,比如输入add(10),打印10
function add(num){
if(num===1){
return 1
}else{
return 1 + add(num-1)
}
}

可以看到,参数比较小的时候一切正常,当参数太大的时候就爆栈了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
add(10)
// 10
add(100000)
// 提示爆栈
VM505:2 Uncaught RangeError: Maximum call stack size exceeded
at add (<anonymous>:2:3)
at add (<anonymous>:5:16)
at add (<anonymous>:5:16)
at add (<anonymous>:5:16)
at add (<anonymous>:5:16)
at add (<anonymous>:5:16)
at add (<anonymous>:5:16)
at add (<anonymous>:5:16)
at add (<anonymous>:5:16)
at add (<anonymous>:5:16)

递归转循环

大部分递归都可以转为循环实现

1
2
3
4
5
6
7
8
9
10
function add(num){
let result = 0
while(num--){
result = result+1
}
return result
}
add(100000)
// 正常输出100000
// 100000

蹦床函数

爆栈的核心在于不停的往调用栈塞入函数,那么我们可以想办法每次递归都执行对应函数,避免调用栈增加

  • 对递归改写

    1
    2
    3
    4
    5
    // 每次返回函数
    function add(num, result=1){
    if(num===1) return result
    return ()=> add(num-1, 1+result)
    }
  • 蹦床函数

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    function trampoline(fun){
    let result = fun()
    // 循环调用,解决递归问题
    while (typeof(result)==='function') {
    result = result()
    }
    return result
    }

    trampoline(add(100000))
    // 返回100000

事件

最后还有一个sao操作,就跟传说中的睡眠排序一样,那就是setTimeout,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
function add(num, result=1){
return new Promise((resolve, reject)=>{
if(num===1) resolve(result)
// 实际上为最少4ms执行一次
setTimeout(function(){
add(num-1, 1+result).then(res=>resolve(res))
}, 0)
})
}
// 能够正常返回100,参数为15000时,也能返回值(证明不会爆栈),就是巨慢...
add(100).then(res=>console.log(res))

相关文章

如何避免 JavaScript 长递归导致的堆栈溢出?
How to avoid Stack overflow error on recursion
CodeReview过程中关于JS代码性能的随想整理

留言

欢迎交流想法。留言会通过 GitHub Issues 保存,首次使用需要登录 GitHub。

模拟koa洋葱模型实现

koa洋葱模型即是注册的中间件采取先进后出的运行策略。

问题

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
app.use(async next => {
console.log(1);
await next();
console.log(2);
});
app.use(async next => {
console.log(3);
await next();
console.log(4);
});

// 异步函数
function fn() {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve("hello");
}, 3000);
});
}
app.use(async next => {
console.log(5);
let res = await fn(); // 调用异步函数
console.log(res)
await next();
console.log(6);
});

app.compose();

实现use与compose方法,预期输出如下结果

1
2
3
4
5
6
7
1
3
5
hello
6
4
2

方法实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
var app = {
middlewares: []
};

// 创建 use 方法
app.use = function(fn) {
app.middlewares.push(fn);
};

app.compose = function() {
// 递归函数
function dispatch(index) {
// 如果所有中间件都执行完跳出
if (index === app.middlewares.length) return Promise.resolve();

// 取出第 index 个中间件并执行
const middleware = app.middlewares[index];
// 核心在这一行
return Promise.resolve(middleware(() => dispatch(index + 1)));
}

// 取出第一个中间件函数执行
dispatch(0);
};

留言

欢迎交流想法。留言会通过 GitHub Issues 保存,首次使用需要登录 GitHub。

Promise重试功能实现

问题描述

使用Promise封装异步请求数据时,当请求失败,可重试指定次数,最好封装一个retry(fun:Promise, times:Number)的重试工具函数

方法实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function retry(fn, times){
return new Promise((resolve, reject)=>{
function run(){
fn().then(resolve).catch(err=>{
if(times--){
console.log(`还有 ${times} 次尝试`)
run()
}else{
reject(err)
}
})
}
run()
})
}

测试样例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
// 每隔一秒生成一个随机数,大于0.9才resolve
function retryDemo(){
return new Promise((resolve, reject)=>{
let r = Math.random()
setTimeout(()=>{
console.log(r)
if(r>0.9){
resolve(r)
}else{
reject('error:'+r)
}
}, 1000)
})
}
// 使用重试函数
retry(retryDemo, 5).then(res=>{
console.log('成功:'+ res)
}).catch(err=>{
console.log(err)
})
/**
* 打印结果如下,5次都失败则打印error
0.13828016742576854
VM642:6 还有 4 次尝试
VM642:21 0.44909079753721226
VM642:6 还有 3 次尝试
VM642:21 0.03058115685015439
VM642:6 还有 2 次尝试
VM642:21 0.29728641790549015
VM642:6 还有 1 次尝试
VM642:21 0.9243906323866069
VM725:2 成功:0.9243906323866069
*/

留言

欢迎交流想法。留言会通过 GitHub Issues 保存,首次使用需要登录 GitHub。