我在微软当 Manager: 1. 成为 Manager

最近有幸成为了团队的一线 manager (M1),准备开一个系列记录我在过程中遇到的挑战和收获,未来可以自我复盘,也希望能够分享给更多的小伙伴,欢迎留言!

我是怎么得到这个机会的

一句话:我的老板离开了,空缺出了 manager 的位置。

作为组内的资深员工,自然有继任的可能性。事实上,我的老板(M1) 跟他的老板(M2)主动推荐了我继承他的家业,然而事情并没有如此顺利和简单,后续经历了长达两三周的沟通和面试筛选。

当然了,我也一直有做 Manager 的准备,加入微软前其实我已经在创业公司做过两次 Tech lead 带 6-10 人团队了(当然创业公司的 Tech lead 和大公司尤其是微软的 Manager 差别还是很大的)。加入微软后,我对微软的每一任老板都讲过,我希望做他们的 backup,加上我足够优(卷)秀(王),每一任老板都给予了我相应的机会:比如参与团队需求立项的过程,带领小团队开发具体某项功能,监督指导团队注重客户事故的复盘。最近一任老板我跟他了三年,尤其在最后一年由于老板负责的领域和人员增加,我成为了其中一个四人小团队的 Squad lead(虚线带人),全面负责某个领域的维护工作:管理该领域相关的 incident(客户事故)和 bug,确保未解决的 incident 和 bug 数量在一个合理范围内,以及确保客户的合理需求(incident, bug 以及邮件沟通)在一定时间内得到满足(否则客户可能会产生不满并向高层提出抱怨)。

我是如何应对老板离开的

在老板决意离开以后,他告诉了他的老板(M2)并很快也告诉了我,我的第一反应是震惊,因为但很快我也意识到这对我来说既是挑战也是机遇,我可以尝试跟随他一起离开,也可以选择找其他新的机会(毕竟最信任我的老板离开了,如果空降其他老板过来,又要花时间精力重新建立信任),还可以选择留下等新老板,由于我在组内的声望/影响力是最多的,新老板一定会仰仗我的协助。此时我并没有觉得我有机会接替他,两个原因:

  1. 我的职级暂时是 Senior 而不是 Principal,而大组最近半年刚组织架构调整,要求每个 Manager 带至少 7 个人,一些带人少的 Manager 就变成了大头兵。我的职级和管理经验相比我老板来说正常境况下不能带很多人,当时我们组里有 11 个人并且还有一两位同事职级比我高。
  2. 我同组以及兄弟组内有不少在此待了 5 – 10 年甚至更久的 Principal 且曾经当过 Manager,所以我当时想的是新老板我应该投同事 L 一票呢还是 B 一票。

两天后,老板带着他的想法来跟我沟通,问我想不想接替他做老板,此时我其实已经有想法想要争取这个机会了,于是我说了 Yes,他就把他的邮件草稿拿给我看,里面是他准备给我的礼物:他起草提议了我们团队可以继续负责的领域以及可以分给其他兄弟团队的领域,这样子我可以少带一些人(尤其是高级别的同事)以及少负责一些领域,以此方案向他老板推荐我接替他。这件事现在回想起来还是一阵暖意,我老板对我的信任可见一斑。

说来也巧,在那段时间我刚参与了课代表立正组织、Howie Xu 主讲的关于 networking 的讲座(付费讲座,花费 149 USD),另一个关于精英主义在组织内是如何起效的 TED 的演讲也对我深有启发(卡拉·哈里斯: 如何找到一位能在工作中帮助你成功的人 | TED Talk)。自老板跟我讲他要离开并且想要推荐我接替他以后,我借此话题开始疯狂的进行 networking。我找了同组内的同事聊我们老板走的话题探探口风,我的前老领导 M2,隔壁大组的 M2,我自己组的 PM 老大,兄弟组的 M1 等。

我有同事在我老板向团队宣布走的会议中间直接对我发消息说“if you get opportunity accept it” (如果你有机会继任我们组的老板,接受它),后续我跟其他同事聊天时很多同事也都主动表示如果我有机会的话,他们会全力支持我,我开玩笑告诉他们可惜 “You do not have power to decide or even vote”。

而前老领导 M2 则直接告诉我应该很直接: Just tell your M2 that you want to become the manager。当然后续还有一些深入的话题:比如问我一些关于决心和信心的问题帮我确认我是否已经准备好投入自己的全身心去做老板(你看,当老板最重要的是 commitment),比如我当时 M2 的风格(老领导跟我当时的 M2 私交很好)。后来老领导跟 M2 当面吃饭的时候提及了我(肯定为我说了一些好话),老领导甚至还半开玩笑半当真的对 M2 说如果 M2 不给我 manager 的话,老领导就要来挖我走去他的新团队了。

另外老领导还实诚的告诉我,他之前在这个组作为 M2 的时候,他心目中这个团队老板的备胎不是我,而是另一个曾经做过 M1 的同事 B(对的,就是我之前在想是否要投他一票做我新老板的人),目前在兄弟组做大头兵,他的原因是我毕竟太嫩了,职级和经验都有待提高。他这段对话刚说前半句后就说 I’m not sure if I should tell you,我直接回 of course please be direct to me,他可能怕他的话会打击我的自信心但事实上我并没有,我并没有去想这些后果,。有趣的是,这次同事 B 也跟我进行了竞争,当然我通过了最终的面试筛选成为了新老板。

兄弟组的 M1 告诉我,最重要的是我要向 M2 表示我有强烈意愿 (willing / passion) 成为老板并且有很大的信心 (confidence) 去管理好团队。一个关键的指标是:M2 要相信把这个团队交给我,他晚上可以安心睡个好觉。

隔壁大组的 M2 做 manager 已经 19 年了(2005 年在微软成为 M1),教给我做好 manager 最重要的一些特质:比如要做好三方面 model, coach and care,比如要 always bring clarity 等等。最后在我的请求之下,他还答应不论我是否这次成功,后续他会继续 mentor 我一段时间帮助我成长成为一个好的 manager。

跟 PM 老大的聊天比较仓促,不过目的一是介绍自己(之前并没有太多沟通),二是从她那里了解团队产品的短期方向和计划(这部分是公开信息)以及为什么这么决策(这部分是私有信息)。这部分聊天虽然仓促,也帮到了我最终的面试:我跟 M3 聊产品时就聊 PM 老大跟我讲的产品方向以及相关的思考,证明我是一个关心产品大方向的领导者而不是一个埋头做事的大头兵。

最终,我带着这些功课来找我的 M2,向他表示我想做老板的决心和信心,稍微聊了聊我对团队的想法,依靠着我老板(和前老领导)的推荐,我的 M2 告诉我我是他的 number 1 choice,然而他并不能独裁钦定,主要是由于当时有一个正在进行中的组织架构调整,具体来说在我们 M3 手下有个中国团队整体转组到美国与我们合并,他们准备来的 Manager 相比于大头兵过剩了,M3 提出为什么不考虑让他们的 Manager 直接空降过来,毕竟相比我来说,他们的管理能力是被验证过的,而我并不是一个被验证过的 Manager,同时 M3 也避免了手下团队某个 manager 变成大头兵而我这个大头兵同时变 Manager 的尴尬(尤其是把中国团队挪到美国来这个已经是对中国团队的一个重大变化,M3 希望能够为他们争取一些机会)。于是 M2 说我们可能需要有一个面试筛选的过程来公平竞争,当然他衷心希望我能赢,因为他也希望团队有机会可以给内部的人选而不是从外部空降,这样更能令大家感觉在这里努力工作是有回报的。

我是如何准备面试问题的

于是 M2 发布了面试招募 Manager 的邮件,他发给了整个大组(总人数大概 500 人)在北美的 Senior 及以上职级的员工(大概有 50 – 100 人符合条件),之后初步筛选了 9 位同事进行面试。面试官 3 人分别是我的 M2,另一个负责客户关系的高级别工程师(直接汇报给我的 M3,与我的 M2 平级),以及我的 M3。经过前两轮面试,9 位同事被筛选出 3 人与 M3 进行最终的面试。

我当时即将离开的 M1 以及 M2 分别对我进行了相应的面试辅导,我觉得对我的面试成功作用也很大。

我的老板主要分享给我两方面的内容:

  1. 回答行为问题的 STAR (Situation, Task, Action, Result) 策略。链接: Amri Celeste – Interview Coach – YouTube.
  2. 微软内部关于企业文化/行为面试的样例问题,这部分由于公司内保密,微软的小伙伴可以在公司内部 SharePoint 上搜索 “Focus areas interview questions printout” 自行查阅。

于是我快速学习了上面 Youtuber 讲解的 STAR 策略,然后针对样例问题准备了我的答案:主要是要准备一些工作中有代表性的故事,很多故事可以被复用在不同的问题上,并且保证这些故事我的三位面试官应该都一下就能明白我当时的挑战是什么。

我的 M2 则是把他对我的正式面试当作了模拟面试(谁让我是他的 number 1 choice 呢,这里我的 M2 有点预设立场的嫌疑了,但我觉得无可厚非,毕竟选出来的人是要作为他的直系下属并且是 manager 岗位,他有偏好是可以理解且被接受的),他问了我一些重要的问题,面试结束给我反馈说哪些问题的答案不是 perfect answer 并给出他的建议。

比如我作为 manager 的优势,我回答的是我是跟着这个组一起成长起来的,了解这个团队的产品和人员,同时我是这个团队最有影响力的员工(历史绩效)并且已经在全面负责一块领域了,我相比于其他人来说是最能够保证团队继续稳定运转的人。他说这个答案不好,明星员工并不一定适合当 manager 并且有些时候团队引入新的人才带来改变并不是坏事,他说这个点是我的 differentiator,他可以来打这张牌但我不应该讲(He can play this card for me but I should not) ,他说我的优势在于 the passion, the drive, the diligence and customer obsession 等等,他相信我是有这些 mindset,希望我能够展现我这些 manager 需要的品质来回答好这个问题。

最后列举一下我被问到的一些问题:

  1. 你为什么想成为 Manager?What’s your passion?
  2. 你觉得你作为 Manager 的优势是什么,劣势是什么?What’s your strength?Weakness?
  3. 你们团队目前遇到的最大的问题是什么?你有什么想法?你为什么现在作为大头兵没有去推进这些想法?为什么非要让你作为 manager 才能完成这些挑战?(这个其实涉及到一个经典的面试问题:Influence without authority,但问我的问题有点像是它的一些反面:with authority, what can you achieve more?)
  4. 讲一讲你是如何应对冲突的?How to resolve conflicts?
  5. 以下你可以选择两个来牺牲,你选哪两个?Work life balance, Rewards, etc. (我忘记其他的了,我选择了这两项作为我的答案并解释)
  6. 未来如果你组内有个年轻的员工举报说跟某个资深员工合作让他很不舒服(比如感觉自己在合作中没有价值感/得到尊重),你会如何应对?
  7. 如果你成为了 Manager,你对团队未来有什么计划?
  8. 如果你没有成为 Manager,你会如何应对?(事实上我之前并没有去想失败了怎么样,有畏难情绪那不是我的风格,所以现场即兴回答了说我会做复盘找差距,用 M2 告诉我的他曾经作为 M1 竞争 M2 失败的故事,他跟着空降来的领导学到了很多,并且半年后又有机会于是他成功成为了 M2,接着表达我会等待下次机会然后紧紧抓住)

整个过程中我做对了什么

  1. 如果你未来想要做 manager,请趁早向你老板以及老板的老板表达你想要做 manager 的想法(就像我对老板们说我希望做他们的 backup),并寻求机会开始锻炼 leadership and management skills,这样在机会来临时你的老板们才会考虑你。
  2. 平时应当注重 reputation 的建立和 networking:精英主义的本质(参见 TED 演讲)就是有 power 的人推荐人来上位,你不仅要有实打实的工作能力,还需要被有 power 的人了解,也就是你需要寻求 sponsor。在我的故事里,我是团队的明星(历年来的绩效考核都是高分),我的老板和我的老领导是我直接的 sponsor。后续我的 M2 被我的 passion 打动,看到我在快速的向各方了解情况为这个职位做准备(比如我的老领导,比如 PM 老大都在日常沟通中向我的 M2 验证过我们之间有沟通),于是他变成了我在 M3 面前的 sponsor(他明确表示我是他的 number 1 choice)。
  3. 我的面试问题准备的很充分,借助于我 M1 和 M2 对我的指导(当然都是我主动发起请求的),我回答问题的结构清晰,我的 M3 在面试完后对我说我有 fantastic communication skills。以及我准备的故事也是比较有说服力的,除了我在这些故事中讲述了自己出色的品质以外,这些故事都是针对三个面试官分别来的(比如 M2 对我强调,跟 M3 面试时候强调我对于产品和客户的专注而不是团队),每位面试官完全明白我在说的故事/解决的问题是什么,也认可我的做法以及反应出的优秀品质。

展望未来

面试告一段落了,趁着我还有记忆,我把这一过程中的关键点都记录了下来,希望对未来的自己和他人都有所帮助。未来在踩坑的路上一定还有更多可以分享的,事实上当 manager 的第一周就遇到几项挑战,未完待续。

微软如何进行年终绩效评定

微软的财年是 7 月到第二年的 6 月,最近我们在做 2023 财年的年度 review,想要可以记录和分享一下微软是如何进行年度 review 的,不如节选出一些我们大组最近 Rewards 大会上 CVP 用的 PPT 直接分享给大家。微软就像个小国家,内部非常多部门/组,各级老板们其实也只是高级打工仔,每个人都希望流程能够尽可能透明,减少猜忌,减少内耗,也有利于公正。

大小公司工作的异同

在微软工作了很多年,结合之前创业公司的感受,是时候总结一下我感受到的大小公司的异同了。就像谈恋爱一样,没有绝对的高低,选择适合自己的才是最重要的。

一、公司文化

在大公司中,公司文化通常表现为更为严谨的组织结构和明确的职责划分。员工在这种环境中往往专注于自己的职责范围,与其他部门的同事交流可能较为有限。此外,大公司往往有更多的规章制度和公司政策,员工需要遵守这些规定,以确保公司的运营和管理秩序。这种公司文化有利于维持高效率和规范化的工作流程。

与大公司相比,小公司的文化通常更加灵活和轻松。在这样的环境中,员工之间的交流和合作更为紧密,每个人在公司中的作用也更为明显。小公司往往鼓励创新和创意,员工可以更快地学会新技能,同时享有更多的自由度。然而,小公司可能缺乏明确的晋升通道和培训机制,员工需要自我驱动,寻找提升的机会。这种公司文化有利于培养员工的自主性和团队精神。

二、发展机会

在大公司工作,员工往往有更多的发展空间。由于公司规模庞大,通常会有更多的职位和晋升机会。大公司往往有明确的职业发展道路,员工可以根据自己的兴趣和能力选择适合的岗位。此外,大公司通常会提供更完善的培训体系,包括内部培训、外部培训、在线课程等,帮助员工提高技能和绩效。在大公司中,优秀的员工更容易获得表现机会和晋升空间。

在小公司工作,员工可能需要承担更多的责任和任务。由于公司规模较小,员工往往需要在多个岗位上兼职,这种情况下,员工可以迅速提高自己的能力和技能。然而,小公司可能没有明确的晋升通道,长期来看,晋升空间可能有限。不过,小公司的员工往往更有机会参与公司的决策和战略规划,从而积累宝贵的管理经验。在小公司中,员工的成长和进步很大程度上取决于个人的努力和机遇。

三、稳定性和待遇

大公司通常具有更强的稳定性,因为它们拥有更多的资源、更广泛的市场份额以及更丰富的经验。在经济波动时期,大公司往往能够更好地抵御风险。此外,大公司往往提供更优厚的福利和薪酬待遇。例如,员工可能会享受到更好的医疗保险、退休计划、年假、育儿假等。此外,大公司常常有严格的薪酬体系,员工的薪资、奖金和福利更有保障。

相对于大公司,小公司的稳定性可能较低,因为它们面临更多的竞争压力和市场波动。在经济不景气时期,小公司可能面临更大的困境。然而,小公司可能提供更具吸引力的激励制度,如股票期权、业绩奖金等。这意味着,如果公司表现良好,员工有可能获得更高的回报。虽然大部分小公司的薪酬和福利可能相对较低,但其员工可以享受到更轻松的工作氛围和更高的工作自由度。在小公司中,员工的收入可能更依赖于个人能力和公司业绩。

四、工作环境和氛围

在大公司中,工作环境通常更为正式和专业。大公司往往有专门的办公区域,为员工提供设备齐全、舒适的工作环境。此外,大公司通常有较完善的福利设施,如健身房、食堂、娱乐设施等。然而,在大公司中,员工可能会感受到更大的竞争压力,与同事之间的关系可能较为疏远。

相较于大公司,小公司的工作环境和氛围往往更为轻松和亲切。员工之间的关系更为紧密,容易建立深厚的友谊。小公司可能没有大公司那样严格的着装要求和工作时间制度,员工可以享受更大的自由度。然而,小公司可能在办公设施和福利设施上不如大公司完善。

五、创新和灵活性

大公司通常具有较强的资源优势,可以投入更多资金和人力进行研发和创新。然而,由于组织结构庞大,大公司的决策过程可能较为缓慢,对新事物的接受和执行速度可能较慢。在大公司中,员工可能需要遵循既定的流程和规定,创新和灵活性相对有限。

小公司往往更具创新精神和灵活性。由于组织结构扁平,决策过程更为迅速,小公司能够快速适应市场变化,实施新的战略和方案。员工在小公司中有更多的自由发挥空间,可以尝试不同的方法和思路。在这样的环境中,员工更容易实现个人价值,为公司创造价值。

六、团队协作和跨部门合作

在大公司中,团队协作和跨部门合作可能会受到组织结构和规章制度的影响。由于公司规模庞大,部门之间的沟通和协作可能较为困难。跨部门合作可能需要经过多个层级的批准和沟通,导致决策和执行效率降低。然而,大公司往往有较为完善的流程和制度,确保不同部门之间的协同工作。在这种环境中,员工需要学会适应公司的组织结构,积极参与团队协作和跨部门合作。

相较于大公司,小公司的团队协作和跨部门合作通常更为顺畅。由于公司规模较小,员工之间的关系更为紧密,沟通和协作更为高效。在小公司中,跨部门合作可能更加灵活,不受过多的流程和制度约束。员工在这样的环境中可以更快地完成任务,提高工作效率。然而,由于资源有限,小公司的员工可能需要在多个项目和任务之间进行平衡,学会高效地利用资源。

七、个人成长与公司成长的关系

在大公司中,个人成长可能与公司的整体发展关系并不是很大。员工虽然可以借助公司的资源和平台,增长自己的见识和认知,然而,在大公司中,员工可能较难脱颖而出,需要付出更多努力才能获得晋升和认可。

而在小公司中,个人成长与公司的发展密切相关。员工的努力和创新直接影响公司的表现,员工可以更明显地看到自己的贡献。在小公司中,个人成长可能更快,但同时也面临更大的风险。

八、如何选择适合自己的公司

在选择大公司还是小公司时,首先要考虑个人的兴趣、能力和职业规划。对于喜欢稳定、有序的工作环境,希望在专业领域深入发展的人来说,大公司可能更为合适。而对于喜欢挑战、追求创新和灵活性的人来说,小公司可能更具吸引力。

此外,还需要考虑公司的发展前景和行业趋势。在有前景的行业和公司中,无论是大公司还是小公司,都有可能为员工提供良好的发展空间。在做出选择时,要充分了解公司的背景、文化和价值观,确保自己的职业发展与公司的愿景相一致。

微软为什么做不好 C 端产品

(免责声明: 本文纯属个人拍脑袋所得,若真的能全面回答这个问题并对症下药,那我肯定也不只是个 Senior Software Engineer 了 🤣 至少得给我个 CVP 的职位不是?🤤)

最近在帮一个学长指导他一款 C 端产品的技术实现,跟他和团队的产品经理进行了一些沟通,对比现在在微软做产品的方式,顿感差别巨大,遂尝试借着这个机会拍脑袋回答一下标题。

学长把公司的员工分成了两个团队,一个负责 C 端产品,一个负责 B 端产品,我说你一创业公司一共没几个人,业务忙起来不应该相互借调么,还用分那么清楚?他说这你就不懂了,C 端产品和 B 端产品需要的技能点和思维方式都是不同的,所以在招聘环节考察的侧重点都不是不同的,说的时髦一点,两个团队基因不同。让负责 C 端的团队成员去做 B 端,或者让负责 B 端的团队成员去做 C 端,都可能会在执行的时候走样,最终搞砸这个产品。

他继续讲,举个例子,C 端产品的特点是注重 UX 细节、快速迭代、用数据验证产品设计,所以负责 C 端产品的产品经理和研发,特别能接受折衷不完美的产品以便快速上线,习惯花时间来做数据埋点及 AB 测试,另外产品经理很懂得看数据指标,等等。

与之形成对比,B 端产品常常需求来源于 B 端客户,负责 B 端的产品经理和研发人员面向个个都惹不起的企业金主客户,一旦跟客户做出承诺基本不能接受产品功能的折衷,甚至哪怕这个功能既不属于主要流程且客户用到的频率很低。此外,由于客户的反馈周期一般较长,较少进行数据埋点和 AB 测试。

讲到这里,我不由得在脑海中对比起 Shopify 和我目前负责研发的产品 Dynamics 365 Commerce,前者面向 C 端或者 SMB,而后者是面向大型企业 B 端客户。我们团队有一段时间面对 Shopify 的火爆是有些危机感的,内部的方向也有些动摇,花了很多时间和人力来打磨我们的 E-Commerce 产品,并有意无意总和 Shopify 来对比比较。疫情这两三年过去了,Shopify 在 SMB 客户体量上继续增长,而我们仍然无法获取到 SMB 客户们的青睐。原因说来也简单,就是太贵了,具体来说有以下几点:

  1. 表面的原因是我们的产品起步价太昂贵。Shopify 可以做到月费 30 美元就建立一个 E-Commerce 网站来销售商品,而我们的产品至少支付数千美元才能起步。
  2. 与起步价格对应的其实是起步成本。客户付费后在 onboarding 阶段,微软一定会指派 Solution Architect (SA) 来协助客户进行整体方案的设计以及初始化配置,因为我们的产品实在是太过复杂,不是一个小白学一个月就能明白的,不信看一下我们官方文档的目录长度可见一斑,甚至我们的产品已经有很多书籍在 Amazon 上售卖了。这意味着客户初始化需要更长的时间和更多的人力损耗。
  3. 最后就是产品使用后的维护成本。这点作为研发在 on-call 的时候我深有(受)体(荼)会(毒),接到客户的 case 后我常常需要花上数小时来查阅资料、试用产品才能定位到问题出现在产品的哪个环节,然后才能去看日志和代码去解决问题。然后我们每年似乎整个产品的 case 数量在 10000 个左右,微软得付出多少成本来维护这个产品,对应客户得有多少运维的人力被消耗掉(跟微软沟通),多少业务场景被卡住,多少销售额因此流失…

当然我们 Dynamics 的产品也不是一无是处 😂 我们仍然是许多超大型客户的首选,比如 UPS,Columbia 等。付出上面这些成本带来的收益是我们的产品支持非常复杂的业务场景比如跨国公司场景下的销售(想象一下多币种、多语言、五花八门的税费规则等),另外可定制化是我们产品的一等需求以方便客户二次开发自己想要的功能(我们有数百家 partner,多数都在细分子行业如医药零售、服装零售等进行了深度的定制化再销售产品给其行业客户)。

上面这段跑偏了,最近我们大老板在员工大会上被问到这个问题:“Shopify 可以让客户花 30 美元月费就开始卖货,我们的竞争力在哪里?”,大老板回复的第一句话就是”It’s not our target market”(这不是我们的目标市场)。你看,产品技术的负责人、领导层对于我们产品的市场定位就是 To Business or Enterprise,所以我们执行层从上到下在设计研发产品时都是抱着服务 B 端客户的思维,因此研发流程类似瀑布形,对待 bug 的容忍度极低因此效率自然打折扣,向后兼容是一条不可触碰的底线因此各项新功能都需要更多的研发时间和测试时间, 这样迭代缓慢而成本又高的产品自然不适用于 C 端客户。

所以总结一下,为什么微软做不好 C 端产品呢?正是由于如上所分析的那样,微软整体自顶向下没有做 C 端产品的 mindset,导致整个组织的行为模式很难设计出好的 C 端产品,最重要的是无法通过收集用户反馈快速迭代来优化产品,最终做出的产品相比于友商没有竞争力。

微软内部跨地区转岗 (Transfer) 流程

应友人请求,总结一下微软内部跨地区转岗的基本流程。

  1. 阅读公司文档
    1. Internal Movement Procedure (sharepoint.com)
    2. Internal Movement – Philosophy and Key Principles (sharepoint.com)
    3. Employee Guide for Transfers (sharepoint.com)
  2. 了解流程后,最重要的是目标地区的团队要有 head count,然后个人就可以在官网 (https://careers.microsoft.com/) 上去投递申请。
    1. 我当时是一个大组内转岗,我直接找我大组的大老板询问是否可以转岗,两个月后大老板推荐了某个有 head count 的小老板(我也认识),问我是否愿意加入,我回答愿意,小老板跟我聊聊后就说欢迎我加入,本来说不需要再面试了,但是后面似乎 HR 需要面评,所以紧急安排了三个人跟我聊,这三个人我也都认识,就随便聊了聊 on call 的问题,还有一些有的没的,没有做算法题,水过之后就发 offer 了。
    2. 在我给大老板的邮件里我给出了他很难拒绝我转岗的四条理由,文章末尾我可以分享一下。
    3. 我的绩效一直都还不错,连续两年 140%。并且早早就有做准备努力在大组有一些 visibility。比如承担了一些内部工具的开发和改进,大组里有人咨询问题我时常主动回复邮件解答,等等。
  3. 面试并拿到 offer 后就会有很多团队来协助你进行后面的事项了,主要有几项需要处理的
    1. L-1 签证,如果你是 Manager,则为 L-1A,否则为 L-1B,L-1 签证在美国不能换工作,只能为微软继续卖命,直到你抽签抽到 H-1B 或者拿到绿卡为止。公司的 immigration team 会通过邮件来跟你沟通,我当时转岗美国总部,是外包给加拿大的 EY (安永) 来帮我准备申请签证所需的材料,他们会提供所需要的完整的材料列表,要求我把所有所需材料上传到微软内部的 (US Immigration Portal) 内。
    2. 由于我主动转岗没有拿到 Relocation package,所以这部分省了。据说常常有两种选项,完全拿一笔现金(需要在美国缴税),或者现金+搬家服务(包括海运,单程机票,头三个月的房租以及租车费等)
    3. 自行购买赴美机票,飞往美国,入境后网上查询自己的 I-94,然后办理入职需要的 I-9,这些都根据微软 immigration team 发来的邮件按步骤做就是了。

附我当时发给大老板的邮件:

智能合约 Hello World

以太坊发展到现在也有很多年了,17 年作为韭菜的我其实买入了不少种类的以太坊上的加密货币(包括一位创业做交易所的学长自己发的空气币🤣),当时对其原理并不了解。最近机缘巧合有机会了解了一下以太坊 (Ethereum) 的智能合约 (Smart Contracts),这里简单记录一下 实现一个智能合约 Hello World 的过程。

以太坊Vitalik 受到比特币的启发后在 2013-2014 年开发出来的一种分布式的区块链应用框架,其官方网站中文自我介绍如下:“以太坊是一种通过支付小额费用向任何人发送加密货币的技术。基于它还能创建永不停机、人人可用的各种应用。它是属于全世界的可编程区块链。

工具链介绍

类似很多编程语言 (Java, C#, etc.) 和很多编程框架 (Spring, ASP.NET, Android, iOS) 一样,以太坊发展到现在有了很多工具链如编程语言 (Solidity),IDE (Remix),本地环境搭建 (HardHat) 等,任何一个工程师都可以很容易的利用这些工具链很方便的上手以太坊智能合约的开发。我这里使用的比较简单,只用到了 VS Code (+Solidity 插件,主要用作语法高亮和格式化代码), npm (本文省略了 npm 的安装过程) 以及 HardHat (稍后解释其作用)。

详细流程

  1. [初始化项目文件夹] 新建一个文件夹命名为你的项目名,然后用 VS Code 打开此文件夹。
  2. [安装 HardHat] 在 VS Code 中打开命令行,运行如下命令来安装 HardHat。
npm install --save-dev hardhat
  1. [初始化 HardHat 本地编程环境] 继续执行如下命令并选择 “Create an empty hardhat.config.js”,到此,我们就有了最基本的本地编程环境。
npx hardhat
  1. [开始编程] 在 ./contracts/ 目录下创建你的第一个智能合约源码文件 Greeter.sol 如下:
pragma solidity ^0.8.0;

contract Greeter {
    string private greeting;

    constructor(string memory _greeting) {
        greeting = _greeting;
    }

    function greet() public view returns (string memory) {
        return greeting;
    }

    function setGreeting(string memory _greeting) public {
        greeting = _greeting;
    }
}
  1. [编译] 使用 HardHat 编译十分简单,在命令行执行如下命令:
npx hardhat compile
  1. [部署] 这里演示如何部署到测试网 (BSC testnet), 参考币安官方文档,配置 hardhat.config.js 如下:
require('@nomiclabs/hardhat-ethers');
require("@nomiclabs/hardhat-etherscan");
require("@nomiclabs/hardhat-waffle");

// This is a sample Hardhat task. To learn how to create your own go to
// https://hardhat.org/guides/create-task.html
task("accounts", "Prints the list of accounts", async (taskArgs, hre) => {
  const accounts = await hre.ethers.getSigners();

  for (const account of accounts) {
    console.log(account.address);
  }
});

module.exports = {
  solidity: {
    version: "0.8.10",
    settings: {
      optimizer: {
        enabled: true,
        runs: 200
      }
    }
  },

  networks: {
    hardhat: {
    },
    bsctest: {
      url: "https://data-seed-prebsc-1-s1.binance.org:8545",
      chainId: 97,
      accounts: ["0x1111111111111111111111111111111111111111111111111111111111111111"]
    },
    bscmain: {
      url: "https://bsc-dataseed.binance.org/",
      chainId: 56,
      accounts: ["0x1111111111111111111111111111111111111111111111111111111111111111"]
    }
  },

  // Config for Etherscan contract verification service.
  // https://hardhat.org/plugins/nomiclabs-hardhat-etherscan.html#usage
  // https://docs.binance.org/smart-chain/developer/deploy/hardhat-verify.html
  etherscan: {
    apiKey: "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
  },

};

之后需要准备部署脚本 scripts/deploy.js

// We require the Hardhat Runtime Environment explicitly here. This is optional
// but useful for running the script in a standalone fashion through `node <script>`.
//
// When running the script with `npx hardhat run <script>` you'll find the Hardhat
// Runtime Environment's members available in the global scope.
const hre = require("hardhat");

// This is a script for deploying your contracts. You can adapt it to deploy
// yours, or create new ones.
async function main() {
  // This is just a convenience check
  if (network.name === "hardhat") {
    console.warn(
      "You are trying to deploy a contract to the Hardhat Network, which" +
      "gets automatically created and destroyed every time. Use the Hardhat" +
      " option '--network localhost'"
    );
  }

  const [deployer] = await hre.ethers.getSigners();
  console.log(
    "Deploying the contracts with the account:",
    await deployer.getAddress()
  );

  console.log("Account balance: ", (await deployer.getBalance()).toString());

  const greeterContractFactory = await hre.ethers.getContractFactory("Greeter");
  const greeterContract = await greeterContractFactory.deploy("Hello world!");
  await greeterContract.deployed();

  console.log("Greeter contract address: ", greeterContract.address);
}

main()
  .then(() => process.exit(0))
  .catch((error) => {
    console.error(error);
    process.exit(1);
  });

最后执行如下命令即可:

npx hardhat run --network bsctest scripts/deploy.js

可以观察到结果

Deploying the contracts with the account: 0x19E7E376E7C213B7E7e7e46cc70A5dD086DAff2A
Account balance:  3440891252400000000
Greeter contract address:  0xf2bf1f1E7B3f8d1554fEeb88B11B69830eDAc7fa
  1. [验证合约] 经过上一步我们已经可以在 Explorer (BSC Testnet Explorer) 里面查看自己刚刚部署好的合约了,但是由于合约是加密的,并没有暴露 ABI 出来所以无法使用 Explorer 直接交互,为方便测试我们可以验证合约,之后所有人都可以直接使用 Explorer 来进行合约交互。为验证合约,我们需要注册 BSC 账户并获取 API Key (链接),之后还是填在 hardhat.config.js 里面,最后执行如下命令完成验证:
npx hardhat verify --network bsctest --contract contracts/Greeter.sol:Greeter 0xf2bf1f1E7B3f8d1554fEeb88B11B69830eDAc7fa "Hello world!"

结果如下,点击最后一个链接便可直接打开 Explorer 连接 MetaMask 进行合约交互了!

Nothing to compile
Compiling 1 file with 0.8.10

Successfully submitted source code for contract
contracts/Greeter.sol:Greeter at 0xf2bf1f1E7B3f8d1554fEeb88B11B69830eDAc7fa
for verification on the block explorer. Waiting for verification result...

Successfully verified contract Greeter on Etherscan.
https://testnet.bscscan.com/address/0xf2bf1f1E7B3f8d1554fEeb88B11B69830eDAc7fa#code

总结

接触智能合约已半年有余,尝试了三四个项目之后每次还是需要重走一遍 hello world 的流程,借此机会总结在这里以便后续翻阅。

其实以太坊智能合约和使用 Spring MVC 或者 ASP.NET 写接口十分相似,以太坊内置了 User Identity / Principal 也就是区块链上的地址,任何智能合约的调用都来源于一个 signer,这个 signer 可以是用户钱包的地址也可以是一个合约地址。智能合约没有数据库,所以简单点可以理解成就是个内存程序,且内存永不销毁,上手起来可比写后端程序简单多了。

后续如果还有时间和精力可以写一些合约的高级开发技巧(比如可升级的合约,微服务合约等)并用与其相对应的后端应用的概念来解读。除了技术方面以外,目前大火的 DeFi(其实已经出现好几年了)也可以用传统金融领域的概念来类比和解读,如做市商,杠杆挖矿等等,十分有趣。

在我看来,区块链这个圈子内的创业者们目前是想要在链上重新构建一个去中心化的金融世界,这对传统中心化的金融世界是一个挑战,在效率上由于去中心化的金融绕过了中心化的金融机构大大降低了交易成本,毕竟这些金融机构左手交右手赚取了大量的利差。若是真的可以再造一个金融世界,这前景不可谓不诱人。而我,只希望作为韭菜不要被两个金融世界来回收割,若能分一杯羹就再好不过了…🤣🤣🤣

要 Transfer 去西雅图了!

最近心情很不平静,不过现在算是跟老婆大人一起把后面至少一两年的行程定了下来,要去美帝了!

上周跟现在的老板摊牌了这件事,老板非但没有任何的刁难,还立刻帮忙介绍他认识的最近 transfer 的同事给我,帮我确保在现在疫情的状况下我的计划可以顺利实现。我发现我工作以来遇到的老板都对我十分友善和支持,在此发自内心再次深深的感谢我的前老板们,希望未来我还能有机会再帮你们搬砖!

在微软上海(2019-09 至今)的这两年,我就职于 D365 Commerce Channel & Employee Management team,如团队名称,负责零售门店和店员的管理这部分的业务,去美国后的团队将是 D365 Commerce Order Operations team,负责零售订单业务(可以说是零售业务的核心了)。

后续就是走流程了,大概是西雅图的老板会给我发 offer(一周之内),我签字后 HR / 律师会来协助我一起申请 L1B 签证(一个月之内准备好各项材料),预约面签(1 周到 1 个月),签证到手(大约 1- 2 周)后就可以准备去西雅图了。

了解了一圈目前的签证状况,L1B 签证在北京、上海都是开放申请的,但总统令 9984(链接) 依然有效,意味着在入境美国之前要在未禁止直接入境的第三国(比如新加坡)待上 14 天才能入境美国。但据刚去的小伙伴说微软的律师会帮忙申请 NIE(链接) 来豁免这项 14 天的限制以便我从中国直飞美国。所以我只能祝自己好运可以豁免了。

最后我想总结一下这次 transfer 的几条原因:

  1. 我希望在美国能够更快更深入地了解和学习我们 Commerce 的业务和团队,美国的 Commerce 团队有很多跟随这个业务一起成长起来的老板 / 工程师,相比上海团队他们有更多的业务知识和理解。由于时差问题,我在上海的时候很多时候了解业务就是通过看代码和文档,效率很低不说,很多历史的代码和文档来龙去脉并不清楚,这阻止、延缓了我在日常工作中做决策的信心和效率,不利于我个人甚至团队的成功。
  2. 我希望我能够成为 D365 Commerce 上海 / 西雅图团队之间的桥梁。过去后我仍然会和原团队有很多交集,我希望我能够使得两边的沟通更加顺畅,所以留在 Commerce 团队对于团队和我个人会是一个双赢的选择。
  3. 英文沟通能力是我应该不断提高的能力之一,尤其作为一个未来要成为 CTO 的男人,去美国工作对我来说是最高效提高相关能力的选择。
  4. 我家老婆大人要去 UW(华盛顿大学西雅图分校)访学,她已经为了我没有申请最匹配的学校、导师了,我也希望能够陪伴她一起。(毕竟我们有过 8 年的异地恋,19 年我来上海微软本身就是为了跟她一起生活…)

工作不易,保持职业发展上升的势头也不易,希望看到我博客的小伙伴都能够工作顺利,不要像我一样如此折腾。

终于建站了!

几年前就跟老朋友扯淡要开自己的博客,今天终于把这个任务完成了,作为第一篇博客,先来记录一下这个不甚顺利的建站过程吧。

为什么选择 WordPress

随手 Google 一下就可以找到诸多博客框架,如 WordPressHexoJekyllHugo 等,促使我选择 WordPress 的理由主要有:

  • 企业更多会选择 WordPress 来构建主页,作为一个要成为 CTO 的男人,我的技术栈都是面向企业而非独立开发者,选择 WordPress (以及后面会提到的 NGINX, Docker 等) 是自然而然的事。
  • WordPress 生态最大,未来即使迁移到其他博客框架上也有很多轮子可以用,上面提到的博客框架都有官方提供的迁移工具:HexoJekyllHugo

建站过程

购买云服务

促使我选择 Azure 云服务的理由只有一个:!毕竟公司每个月 150 USD 的羊毛不用白不用。个人认为 Azure Portal 还算好用,比 AWS 的 UX 顺滑那么一些…

在 Azure Portal 注册以及绑定 billing 信息后,搜索如下资源进行购买

资源名称价格购买流程文档
域名 (frankzhang.net)每年 11.99 USD 起搜索 “App Service Domains” 进行购买Buy a custom domain name – Azure App Service | Microsoft Docs
虚拟机
(Azure Ubuntu VM)
B2S (双核 4 GB) 每月 38.544 USD搜索 “Virtual Machines” 进行购买Quickstart – Create a Linux VM in the Azure portal – Azure Virtual Machines | Microsoft Docs
数据库
(Azure Database for MySQL servers)
0想什么呢,云数据库你用得起么?🤣MySQL :: A Quick Guide to Using the MySQL APT Repository

购买好上述服务后,需要将域名绑定到对应虚拟机的 IP 上,在 Azure Portal 上只需打开 VM 对应的 public IP 资源,在其配置中增加一条 alias record (A 记录),如下我添加了 blog.frankzhang.net 指向此 IP

安装 MySQL

如上面表格罗列的,由于我买不起云数据库,只好自行在 Azure VM 中安装 MySQL,参照官方文档,但 Azure Ubuntu VM 似乎已经包含了 MySQL 的 apt repository,所以只需下面两行命令搞定

sudo apt update
sudo apt install mysql-server

由于 MySQL 默认只监听 localhost 的 3306 端口,下面的命令修改其监听任意来源而不仅仅是 localhost。不用担心,云服务的网络设置默认只开放 22 端口,未来我们还会放开 80 和 443 端口,但无论如何 3306 端口是不会被外网所访问到的。这步卡了我很久,由于我的 WordPress 是安装在 Docker 容器内,在后续的安装中显示 “Error establishing a database connection”,从怀疑 MySQL 用户密码错误到怀疑 Azure 内网设置不正确,最后才发现是由于 MySQL “bind_address” 这个默认配置。

echo 'bind-address = 0.0.0.0' | sudo tee -a /etc/mysql/mysql.conf.d/mysqld.cnf
sudo systemctl restart mysql

这里我选择在宿主机上直接安装 MySQL 而非在 Docker 中,是因为在企业中我们都会购买云数据库 (RDS),其架构是远程/内网有一或多台数据库服务器,在宿主机上直接安装 MySQL 模拟了这样的网络架构。(哈哈不吹了,其实是因为这样最简单也更偏向于企业内的用法,没听说过哪家线上 MySQL 是用 Docker image 来启动的。另外最近听朋友说到一个惨案,他习惯使用 docker 删除操作,一不小心把数据 volume 也删了…)

此处省略了创建数据库和用户(给 WordPress 专用)的步骤,请自行脑补。

安装 Docker

都 2021 年了,哪个公司的服务器上还能不安装 Docker 呢?(好吧微软的 Windows 服务器的确不用 Docker…)

继续根据官方文档,一顿操作即可

# Set up repository, can be ignored in cloud ubuntu vm.
sudo apt update
sudo apt install apt-transport-https ca-certificates gnupg lsb-release
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg
echo \
  "deb [arch=amd64 signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu \
  $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null

# Install Docker Engine
sudo apt update
sudo apt install docker-ce docker-ce-cli containerd.io

安装 WordPress

官方文档的安装方式需要提前在服务器上安装好 PHP,由于我不清楚 PHP 为什么是世界上最好的语言,抱着敬而远之的态度,我选择使用 Docker 来帮我隔离掉这项我不甚了解的技术,所幸 WordPress Community 维护WordPress Docker Images(这就是选择大生态工具的好处),于是事情变得十分简单。

cd /var/www/blog.frankzhang.net
docker pull wordpress
docker run -e WORDPRESS_DB_HOST=<host_ip>:3306 -e WORDPRESS_DB_USER=<username> -e WORDPRESS_DB_PASSWORD=<password> --name wordpress -p 8181:80 -v "$PWD/html":/var/www/html -d wordpress

上述参数的简单解释

  • –name,给容器起个名字
  • -p,指定端口映射,格式为:宿主机端口:容器端口,后续我会使用 NGINX 来反向代理此宿主机端口。
  • -v,映射宿主机一个 volume 到容器内,用这种方式即使 WordPress Docker 容器 / image 被删除,宿主机上的文件仍然存在,Wordpress Docker 容器 /var/www/html 目录内存放 WordPress 的 PHP 代码,主题及配置,图片和视频等多媒体文件等。
  • -d,在后台运行容器。
  • -e 环境参数皆在 Docker Hub WordPress Image 主页里有详细介绍,这里不再赘述。

至此,咱们的 WordPress 已经可以在内网访问了,通过 curl localhost:8181 应该可以看到返回的 WordPress html 网页了。

安装 NGINX

关于 NGINX 是否应该通过 Docker 容器来运行,暂时我认为是否定的,有以下几个原因

  1. NGINX 不像应用那样改动 / 升级频繁
  2. NGINX 本身的安装十分简便 (apt install nginx)
  3. 我想要使用 Certbot 自动更新 SSL 证书,使用容器不方便做到。

惯例:官方文档,不过这次我是按照这篇教程来的,安装 NGINX 后要记得去 Azure Portal 将网络设置修改一下,允许外网访问 80 和 443 端口,如下

然后可以去写入 NGINX 配置文件了,sudo vi /etc/nginx/sites-available/blog.frankzhang.net 填入如下配置,之后记得在 /etc/nginx/sites-enabled 目录下创建一个到此配置的软链接。

server {
    listen 80;
    server_name blog.frankzhang.net;
    return 301 https://blog.frankzhang.net$request_uri; # 将 HTTP 请求都转至 HTTPS
}
server {
    listen 443 ssl http2;
    server_name blog.frankzhang.net;

    ssl_session_timeout 5m;
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_ciphers ECDHE-RSA-AES256-GCM-SHA512:DHE-RSA-AES256-GCM-SHA512:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-SHA384;
    ssl_prefer_server_ciphers on;
    ssl_session_cache shared:SSL:10m;

    location / {
        proxy_pass http://localhost:8181;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto https;
    }
}

其实上述配置的核心就一句话,将 443 端口的请求转发至 localhost:8181,Docker 来运行 WordPress 真香,否则还要去配置什么我并不大懂的 PHP FastCGI。

最后一步便是安装 SSL 证书了,作为个人用户咱们就省点钱用用免费的 Certbot (Let’s Encrypt) 好了,命令也十分简单,如下

sudo apt install certbot
sudo certbot --nginx

然后按照其指示安装即可,安装成功后如果设置了自动 renew,Certbot 会在 /etc/cron.d 目录下放置一个脚本,每 12 小时尝试更新一次。

初始化 WordPress

第一次访问 https://blog.frankzhang.net,Wordpress 会出现配置页面,根据指示不断的下一步,大功告成!

结语

在整个过程中遇到的几个坑

  1. MySQL 默认只监听 localhost 3306 端口,通过内网 IP 不可访问。
  2. 网上很多 WordPress 和 NGINX 的教程,基本都会跟你讲 FastCGI 在 NGINX 内的相关配置,把我搞得晕头转向。其实由于咱们使用了 WordPress 默认的 Docker image,内部已经用 Apache server 来 host PHP 提供 HTTP 服务了,所以只需要无脑转发 HTTP 请求即可。