<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>xiaoronglv (吕小荣)</title>
    <link>https://ruby-china.org/xiaoronglv</link>
    <description>社区清洁工</description>
    <language>en-us</language>
    <item>
      <title>[上海][2026 年 03 月 10 日] AI Tuesday: 一起来吃龙虾 OpenClaw</title>
      <description>&lt;h2 id="主题：OpenClaw"&gt;主题：OpenClaw&lt;/h2&gt;
&lt;p&gt;讨论主题有：&lt;/p&gt;

&lt;p&gt;主题一：OpenClaw 落地&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;我遇到了了什么商业以及生活中的问题&lt;/li&gt;
&lt;li&gt;openclaw 如何帮我解决这个问题的过程&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;主题二：OPC 创业&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;我组建了一家 opc 公司&lt;/li&gt;
&lt;li&gt;我用 openclaw 替换了哪些员工，如何替换的&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="时间地点"&gt;时间地点&lt;/h2&gt;
&lt;p&gt;时间：2026 年 03 月 10 日 (周二) 19:45 - 21:15&lt;/p&gt;

&lt;p&gt;地点：上海市静安区南京西路 699 号 东方投资大厦 26 楼 Faria 办公室 (地铁 2 号线南京西路地铁站)&lt;/p&gt;
&lt;h2 id="在线拨入"&gt;在线拨入&lt;/h2&gt;
&lt;p&gt;点击链接直接加入会议：&lt;/p&gt;

&lt;p&gt;&lt;a href="https://meeting.tencent.com/p/7710846366" rel="nofollow" target="_blank"&gt;https://meeting.tencent.com/p/7710846366&lt;/a&gt;&lt;/p&gt;
&lt;h2 id="建议肉身参加"&gt;建议肉身参加&lt;/h2&gt;
&lt;p&gt;这是会议室风景图&lt;/p&gt;

&lt;p&gt;&lt;img src="https://l.ruby-china.com/photo/ericguo/f43febb5-7fb7-4096-8b8b-d2245a2edcb0.jpg!large" title="" alt=""&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src="https://l.ruby-china.com/photo/ericguo/f65113dc-8ec0-4cca-bb1e-0b6b7c8fda60.jpg!large" title="" alt=""&gt;&lt;/p&gt;</description>
      <author>xiaoronglv</author>
      <pubDate>Wed, 04 Mar 2026 11:11:51 +0800</pubDate>
      <link>https://ruby-china.org/topics/44503</link>
      <guid>https://ruby-china.org/topics/44503</guid>
    </item>
    <item>
      <title>上海 OPC (one person company) 群，欢迎创业者加入，互通有无</title>
      <description>&lt;p&gt;最近上海及国家层面针对 One Person Company (OPC) 陆续出台了多项扶持政策，重点覆盖以下领域：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;场地支持：专项办公空间补贴或入驻优惠。&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;算力支持：针对技术型初创企业的算力资源对接。&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;财税支持：记账、报税流程简化及费用减免。&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;线下创业者的见面分享会&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;为了帮助大家更高效地获取政策信息、降低运营成本，我组建了上海 OPC 微信群，大家互通有无。&lt;/p&gt;

&lt;p&gt;&lt;img src="https://l.ruby-china.com/photo/xiaoronglv/4a32e920-1b5f-4fdf-8e7a-f8fc1556b270.png!large" title="" alt=""&gt;&lt;/p&gt;
&lt;h2 id="参考资料"&gt;参考资料&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://mp.weixin.qq.com/s/jAVfU7cq3bueIEPlI_dxtA" rel="nofollow" target="_blank"&gt;https://mp.weixin.qq.com/s/jAVfU7cq3bueIEPlI_dxtA&lt;/a&gt;&lt;/p&gt;</description>
      <author>xiaoronglv</author>
      <pubDate>Thu, 12 Feb 2026 12:23:53 +0800</pubDate>
      <link>https://ruby-china.org/topics/44479</link>
      <guid>https://ruby-china.org/topics/44479</guid>
    </item>
    <item>
      <title>[上海][2025 年 6 月 10 日] Ruby 线下聚会：AI 产品“捏 Ta”的技术架构</title>
      <description>&lt;p&gt;时间：2025 年 6 月 10 日 (周二) 19:45 - 21:15 &lt;/p&gt;

&lt;p&gt;地点：上海市静安区凤阳路 707 号 KYMS 汇银大厦 9 楼演播厅   (地铁 2 号线南京西路地铁站)&lt;/p&gt;
&lt;h2 id="主题 &amp;amp; 讲师"&gt;主题 &amp;amp; 讲师&lt;/h2&gt;
&lt;p&gt;讲师：王俊喆（捏 Ta CTO）&lt;/p&gt;

&lt;p&gt;主题：图像视频相关推理端的一些折腾，如何调教几百个 4090 让它每天伸长百万张图片。&lt;/p&gt;

&lt;p&gt;产品：&lt;a href="https://app.nieta.art/" rel="nofollow" target="_blank" title=""&gt;捏它&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;技术栈&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;推理端：ComfyUI&lt;/li&gt;
&lt;li&gt;后端：Python Fast API + Dramatiq&lt;/li&gt;
&lt;li&gt;前端：Flutter&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="参会可以获得的礼品"&gt;参会可以获得的礼品&lt;/h2&gt;
&lt;p&gt;参加的朋友有机会获得一张 Jetbrain 兑换券，可以兑换 Jetbrain 的一个产品一年的使用。只有 1 个 license，抽奖决定。&lt;/p&gt;
&lt;h2 id="在线拨入"&gt;在线拨入&lt;/h2&gt;
&lt;p&gt;无法肉身参加的同学，可以通过 Google Meeting 拨入。&lt;/p&gt;

&lt;p&gt;Tuesday, June 10 · 19:45 – 21:45&lt;br&gt;
Video call link: &lt;a href="https://meet.google.com/iuk-nkno-pqy" rel="nofollow" target="_blank"&gt;https://meet.google.com/iuk-nkno-pqy&lt;/a&gt;&lt;/p&gt;
&lt;h2 id="感谢"&gt;感谢&lt;/h2&gt;
&lt;p&gt;感谢 Fariaedu 赞助场地。&lt;/p&gt;
&lt;h2 id="Reference"&gt;Reference&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href="https://app.nieta.art/" rel="nofollow" target="_blank" title=""&gt;捏它&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;下载链接：&lt;a href="https://sj.qq.com/appdetail/art.nieta.app" rel="nofollow" target="_blank"&gt;https://sj.qq.com/appdetail/art.nieta.app&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.thepaper.cn/newsDetail_forward_26978376" rel="nofollow" target="_blank" title=""&gt;下载量超 2.5 亿次，捏 Ta“人生人”平台爆火，二次元狂喜&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;</description>
      <author>xiaoronglv</author>
      <pubDate>Thu, 29 May 2025 11:23:28 +0800</pubDate>
      <link>https://ruby-china.org/topics/44179</link>
      <guid>https://ruby-china.org/topics/44179</guid>
    </item>
    <item>
      <title>[上海][2025 年 5 月 27 日] Ruby 线下聚会：RubyLLM &amp; Dify</title>
      <description>&lt;p&gt;时间：2025 年 5 月 27 日 (周二) 19:45 - 21:15（注意时间比之前的晚了半个小时，因为大家反馈现在太卷下班要卷一下）&lt;/p&gt;

&lt;p&gt;地点：上海市静安区凤阳路 707 号 KYMS 汇银大厦 9 楼演播厅   (地铁 2 号线南京西路地铁站)&lt;/p&gt;
&lt;h2 id="主题 &amp;amp; 讲师"&gt;主题 &amp;amp; 讲师&lt;/h2&gt;
&lt;p&gt;讲师：Eric&lt;/p&gt;

&lt;p&gt;主题：智能合同关键信息抽取，使用 dify 实现自动化&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://rubyllm.com/" rel="nofollow" target="_blank" title=""&gt;RubyLLM&lt;/a&gt;: A delightful Ruby way to work with AI&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dify.ai/" rel="nofollow" target="_blank"&gt;https://dify.ai/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="参会可以获得的礼品"&gt;参会可以获得的礼品&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;Jetbrain 兑换券，可以兑换 Jetbrain 的一个产品一年的使用。只有 1 个 license，抽奖决定。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;img src="https://l.ruby-china.com/photo/xiaoronglv/86ab8035-6f8e-45a6-b0f6-890a330ce7bf.png!large" title="" alt=""&gt;&lt;/p&gt;
&lt;h2 id="在线拨入"&gt;在线拨入&lt;/h2&gt;
&lt;p&gt;无法肉身参加的同学，可以通过 Google Meeting 拨入。&lt;/p&gt;

&lt;p&gt;Video call link: &lt;a href="https://meet.google.com/kyk-hrcd-yho" rel="nofollow" target="_blank"&gt;https://meet.google.com/kyk-hrcd-yho&lt;/a&gt;&lt;/p&gt;
&lt;h2 id="感谢"&gt;感谢&lt;/h2&gt;
&lt;p&gt;感谢 Fariaedu 赞助场地。&lt;/p&gt;
&lt;h2 id="工作机会 Jobs &amp;amp; Opportunities"&gt;工作机会 Jobs &amp;amp; Opportunities&lt;/h2&gt;
&lt;p&gt;todo&lt;/p&gt;</description>
      <author>xiaoronglv</author>
      <pubDate>Wed, 21 May 2025 10:33:30 +0800</pubDate>
      <link>https://ruby-china.org/topics/44169</link>
      <guid>https://ruby-china.org/topics/44169</guid>
    </item>
    <item>
      <title>[上海][2025 年 3 月 25 日] Ruby 线下聚会 - AI Coding 专项主题</title>
      <description>&lt;p&gt;时间：2025 年 3 月 25 日 (周二) 19:45 - 21:15（注意时间比之前的晚了半个小时，因为大家反馈现在太卷下班要卷一下）&lt;/p&gt;

&lt;p&gt;地点：上海市静安区凤阳路 707 号 KYMS 汇银大厦 9 楼会议室 A   (地铁 2 号线南京西路地铁站)&lt;/p&gt;
&lt;h2 id="主题 &amp;amp; 讲师"&gt;主题 &amp;amp; 讲师&lt;/h2&gt;
&lt;p&gt;讲师：李亚飞&lt;/p&gt;

&lt;p&gt;&lt;img src="https://l.ruby-china.com/photo/xiaoronglv/51b30f2c-f868-4c83-b459-1c1f80e077df.png!large" title="" alt=""&gt;&lt;/p&gt;
&lt;h2 id="参会可以获得的礼品"&gt;参会可以获得的礼品&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;Jetbrain 兑换券，可以兑换 Jetbrain 的一个产品一年的使用。只有 1 个 license，抽奖决定。&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="腾讯会议"&gt;腾讯会议&lt;/h2&gt;
&lt;p&gt;无法肉身参加的同学，可以通过腾讯会议拨入。&lt;/p&gt;

&lt;p&gt;会议时间：2025/03/25 19:45-21:15 (GMT+08:00) 中国标准时间 - 北京&lt;/p&gt;

&lt;p&gt;腾讯会议：557-900-994&lt;/p&gt;

&lt;p&gt;点击链接
&lt;a href="https://meeting.tencent.com/dm/XiHwcVc2rQSI" rel="nofollow" target="_blank"&gt;https://meeting.tencent.com/dm/XiHwcVc2rQSI&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;(感谢斌哥帮忙设置腾讯会议的会员链接）&lt;/p&gt;
&lt;h2 id="感谢"&gt;感谢&lt;/h2&gt;
&lt;p&gt;感谢 Fariaedu 赞助场地。&lt;/p&gt;</description>
      <author>xiaoronglv</author>
      <pubDate>Mon, 24 Mar 2025 16:46:01 +0800</pubDate>
      <link>https://ruby-china.org/topics/44105</link>
      <guid>https://ruby-china.org/topics/44105</guid>
    </item>
    <item>
      <title>[上海][2025 年 3 月 11 日] Ruby 线下聚会 </title>
      <description>&lt;p&gt;时间：2025 年 3 月 11 日 (周二) 19:45 - 21:15（注意时间比之前的晚了半个小时，因为大家反馈现在太卷下班要卷一下）&lt;/p&gt;

&lt;p&gt;地点：上海市静安区凤阳路 707 号 KYMS 汇银大厦 9 楼演播厅   (地铁 2 号线南京西路地铁站)&lt;/p&gt;
&lt;h2 id="主题 &amp;amp; 讲师"&gt;主题 &amp;amp; 讲师&lt;/h2&gt;
&lt;p&gt;讲师：杜睿&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;自我介绍&lt;/li&gt;
&lt;li&gt;简介现阶段通用多模态

&lt;ul&gt;
&lt;li&gt;简介训练方法&lt;/li&gt;
&lt;li&gt;优点和不足&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;介绍医用多模态大模型的发展

&lt;ul&gt;
&lt;li&gt;现阶段的数据集&lt;/li&gt;
&lt;li&gt;和通用大模型的区别&lt;/li&gt;
&lt;li&gt;现阶段主要研究方向&lt;/li&gt;
&lt;li&gt;未来可能的发展方向&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="参会可以获得的礼品"&gt;参会可以获得的礼品&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;无限贴纸供应&lt;/li&gt;
&lt;li&gt;Jetbrain 兑换券，可以兑换 Jetbrain 的一个产品一年的使用。只有 1 个 license，抽奖决定。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;img src="https://l.ruby-china.com/photo/xiaoronglv/86ab8035-6f8e-45a6-b0f6-890a330ce7bf.png!large" title="" alt=""&gt;&lt;/p&gt;
&lt;h2 id="腾讯会议"&gt;腾讯会议&lt;/h2&gt;
&lt;p&gt;无法肉身参加的同学，可以通过腾讯会议拨入。&lt;/p&gt;

&lt;p&gt;会议主题：Ruby Tuesday&lt;/p&gt;

&lt;p&gt;点击链接直接加入会议：
&lt;a href="https://meeting.tencent.com/p/8475477212" rel="nofollow" target="_blank"&gt;https://meeting.tencent.com/p/8475477212&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;#腾讯会议：847-547-7212
会议密码：291315&lt;/p&gt;
&lt;h2 id="感谢"&gt;感谢&lt;/h2&gt;
&lt;p&gt;感谢 Fariaedu 赞助场地。&lt;/p&gt;</description>
      <author>xiaoronglv</author>
      <pubDate>Fri, 07 Mar 2025 17:34:55 +0800</pubDate>
      <link>https://ruby-china.org/topics/44084</link>
      <guid>https://ruby-china.org/topics/44084</guid>
    </item>
    <item>
      <title>[上海][2025 年 2 月 18 日] Ruby 线下聚会 - 使用 Dify 做 AI 应用</title>
      <description>&lt;p&gt;时间：2025 年 2 月 18 日 (周二) 19:45 - 21:15（注意时间比之前的晚了半个小时，因为大家反馈现在太卷下班要卷一下）&lt;/p&gt;

&lt;p&gt;地点：上海静安区南京西路 819 号 4 楼 Wework - 4J 会议室  (地铁 2 号线南京西路地铁站)&lt;/p&gt;
&lt;h2 id="主题 &amp;amp; 讲师"&gt;主题 &amp;amp; 讲师&lt;/h2&gt;
&lt;p&gt;主题：如何使用 Dify 做 AI 应用&lt;/p&gt;

&lt;p&gt;讲师：Eric&lt;/p&gt;

&lt;p&gt;&lt;img src="https://l.ruby-china.com/photo/xiaoronglv/064e3054-6f6c-4e5e-872f-5803f08b8316.png!large" title="" alt=""&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src="https://l.ruby-china.com/photo/xiaoronglv/cfdd9c72-b198-4a61-836b-0aa8bce0a151.png!large" title="" alt=""&gt;&lt;/p&gt;
&lt;h2 id="参会可以获得的礼品"&gt;参会可以获得的礼品&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;无限贴纸供应&lt;/li&gt;
&lt;li&gt;Jetbrain 兑换券，可以兑换 Jetbrain 的一个产品一年的使用。只有 2 个 license，抽奖决定。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;img src="https://l.ruby-china.com/photo/xiaoronglv/86ab8035-6f8e-45a6-b0f6-890a330ce7bf.png!large" title="" alt=""&gt;&lt;/p&gt;
&lt;h2 id="腾讯会议"&gt;腾讯会议&lt;/h2&gt;
&lt;p&gt;无法肉身参加的同学，可以通过腾讯会议拨入。&lt;/p&gt;

&lt;p&gt;会议主题：Ruby Tuesday&lt;/p&gt;

&lt;p&gt;会议时间：2025/02/18 19:45-20:15 (GMT+08:00) 中国标准时间 - 北京&lt;/p&gt;

&lt;p&gt;点击链接入会，或添加至会议列表：
&lt;a href="https://meeting.tencent.com/dm/E2aD6YW71VNE" rel="nofollow" target="_blank"&gt;https://meeting.tencent.com/dm/E2aD6YW71VNE&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;#腾讯会议：633-970-818&lt;/p&gt;</description>
      <author>xiaoronglv</author>
      <pubDate>Fri, 14 Feb 2025 12:20:06 +0800</pubDate>
      <link>https://ruby-china.org/topics/44049</link>
      <guid>https://ruby-china.org/topics/44049</guid>
    </item>
    <item>
      <title>[上海][半远程] Workstream 招聘 Staff Ruby Engineer</title>
      <description>&lt;h2 id="About Workstream"&gt;About Workstream&lt;/h2&gt;
&lt;p&gt;Workstream is a mission-driven company that believes in building premium, modern software solutions for local businesses. There are 2.7 billion hourly workers, who make up 80% of the global workforce, but they've been heavily underserved by technology and deserve better. We help local businesses around you hire, manage, and retain qualified workers.&lt;/p&gt;

&lt;p&gt;Our customers include leading brands from multiple sectors, including Burger King, Carl's Jr./Hardee's, IHOP, KFC, and Culvers. At series B, we are quickly expanding our product portfolio. We are backed by legendary VCs and industry experts like Founders Fund, BOND, and Coatue.&lt;/p&gt;

&lt;p&gt;Our engineering team is composed of passionate, driven, and talented engineers who focus on building and innovating cloud technology solutions that empower local businesses to thrive and better serve the deskless workforce in their communities. We are seeking a talented Staff Software Engineer to join our growing engineering team in Shanghai.&lt;/p&gt;
&lt;h2 id="Day in the Life:"&gt;Day in the Life:&lt;/h2&gt;
&lt;p&gt;As a Staff Engineer, you will:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Work with your squad team members (local + remote) to ensure that implementations are consistent with the overall Engineering team strategy.&lt;/li&gt;
&lt;li&gt;Work closely with the Engineering Manager on communicating tasks, challenges, and workflows across a globally distributed team.&lt;/li&gt;
&lt;li&gt;Contribute to the design and architecting of cutting-edge tools and technologies while maintaining the existing stack.&lt;/li&gt;
&lt;li&gt;Guide team development efforts toward successful sprint delivery.&lt;/li&gt;
&lt;li&gt;Maintain high software quality standards within the team by establishing good habits and practices.&lt;/li&gt;
&lt;li&gt;Provide technical leadership to team members through mentoring and coaching.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="Who You Are:"&gt;Who You Are:&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Advanced skills in Ruby with 5+ years of experience.&lt;/li&gt;
&lt;li&gt;8+ years of industry experience as an individual contributor in web application development&lt;/li&gt;
&lt;li&gt;Advanced skills in JavaScript or Go&lt;/li&gt;
&lt;li&gt;3+ years of experience with relational databases such as PostgreSQL.&lt;/li&gt;
&lt;li&gt;5+ years of experience building and maintaining RESTful APIs.&lt;/li&gt;
&lt;li&gt;Experience developing and maintaining microservice-based systems is a bonus.&lt;/li&gt;
&lt;li&gt;2+ years of experience working in Agile software engineering methods and tools: Scrum, Jira, GitHub, DevOps, and software architectures that allow rapid iterations&lt;/li&gt;
&lt;li&gt;Demonstrate an understanding of requirement management and roadmap development with product managers&lt;/li&gt;
&lt;li&gt;Possess a passion for effective collaboration within and between cross-functional teams, with a history of building influence with Product and Design&lt;/li&gt;
&lt;li&gt;Familiarity with cloud technologies (AWS) and architectural patterns and their trade-offs&lt;/li&gt;
&lt;li&gt;Experience working with globally distributed engineering teams is an asset&lt;/li&gt;
&lt;li&gt;Passionate to learn, understand, and dissect new technology stacks quickly on your own&lt;/li&gt;
&lt;li&gt;Bachelor's degree from a four-year college or university; or equivalent training, education, and experience in information / cyber security, computer systems, IT, etc.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="Our Tech Stacks:"&gt;Our Tech Stacks:&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Backend&lt;/strong&gt;: Ruby on Rails / Go / Nodejs&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Frontend&lt;/strong&gt;: React / Typescript&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Database&lt;/strong&gt;: PostgreSQL / Redis / DynamoDB&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Infra&lt;/strong&gt;: AWS / Terraform / Kubernetes / Protobuf / Kafka / Elasticsearch&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="What We Offer:"&gt;What We Offer:&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;A mission-driven and value-based company dedicated to empower deskless workers and local businesses&lt;/li&gt;
&lt;li&gt;An early employee opportunity at a Series B hyper-growth startup; work with the founding team and industry veterans to accelerate your career&lt;/li&gt;
&lt;li&gt;Competitive salary and equity&lt;/li&gt;
&lt;li&gt;Comprehensive health coverage&lt;/li&gt;
&lt;li&gt;Learning/development stipend&lt;/li&gt;
&lt;li&gt;Unlimited PTO&lt;/li&gt;
&lt;li&gt;Hybrid Office/WFH schedule &lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="工作地址"&gt;工作地址&lt;/h2&gt;
&lt;p&gt;上海静安区南京西路 819 号 Wework  (地铁 2 号线南京西路地铁站)&lt;/p&gt;

&lt;p&gt;工作方式为混合式办公。&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;周一/周五去上海南京西路的办公室上班&lt;/li&gt;
&lt;li&gt;周二/周三/周四 灵活办公 (鼓励去办公室)&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="薪酬范围"&gt;薪酬范围&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;70 万 -100 万 RMB / 年&lt;/li&gt;
&lt;li&gt;有期权&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="联系方式:"&gt;联系方式：&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;简历必须为中英文&lt;/li&gt;
&lt;li&gt;投递简历时请备注来自 Ruby-China 论坛，HR 会优先处理。&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;img src="https://l.ruby-china.com/photo/xiaoronglv/4ed809a0-9b7e-4058-b357-eaa98570006f.png!large" title="" alt=""&gt;&lt;/p&gt;

&lt;p&gt;Email: 
&lt;img src="https://l.ruby-china.com/photo/xiaoronglv/8fa91700-4d6e-4848-afb1-6298b9b1d08c.png!large" title="" alt=""&gt;&lt;/p&gt;
&lt;h2 id="队友"&gt;队友&lt;/h2&gt;
&lt;p&gt;美国 San Francisco Office&lt;/p&gt;

&lt;p&gt;&lt;img src="https://l.ruby-china.com/photo/xiaoronglv/ae788d89-07ac-4323-b0d5-66f9bd88bc36.png!large" title="" alt=""&gt;&lt;/p&gt;

&lt;p&gt;Shanghai Office&lt;/p&gt;

&lt;p&gt;&lt;img src="https://l.ruby-china.com/photo/xiaoronglv/940cd753-300a-42f3-9bd2-1e122fe7f388.png!large" title="" alt=""&gt;&lt;/p&gt;

&lt;p&gt;Chengdu Office&lt;/p&gt;

&lt;p&gt;&lt;img src="https://l.ruby-china.com/photo/xiaoronglv/b421a379-c4ac-4b0e-8bd0-cb647843b06f.png!large" title="" alt=""&gt;&lt;/p&gt;</description>
      <author>xiaoronglv</author>
      <pubDate>Fri, 14 Feb 2025 09:49:43 +0800</pubDate>
      <link>https://ruby-china.org/topics/44048</link>
      <guid>https://ruby-china.org/topics/44048</guid>
    </item>
    <item>
      <title>推荐三个教小孩学编程的 App</title>
      <description>&lt;p&gt;我在教我儿子编程，不能一上来就教他 if else, loop, object 等概念，要不然吓到他，就没办法教下去了。&lt;/p&gt;

&lt;p&gt;所以在找一些趣味性的软件，争取寓教于乐，暂时找到了 3 个，还在调研，稍后会整理出来。&lt;/p&gt;
&lt;h2 id="Kodable"&gt;Kodable&lt;/h2&gt;
&lt;p&gt;收费：订阅制，或一次性买断。&lt;/p&gt;

&lt;p&gt;官网：&lt;a href="https://www.kodable.com/" rel="nofollow" target="_blank" title=""&gt;https://www.kodable.com/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;这个软件的后台居然是用 Ruby 语言写的，还在招聘 Ruby 程序员。同行碰同行，真是惊喜。&lt;/p&gt;
&lt;h2 id="Code Spark"&gt;Code Spark&lt;/h2&gt;
&lt;p&gt;收费：订阅制&lt;/p&gt;

&lt;p&gt;官网：&lt;a href="https://codespark.com/" rel="nofollow" target="_blank" title=""&gt;https://codespark.com/&lt;/a&gt;&lt;/p&gt;
&lt;h2 id="Scratch"&gt;Scratch&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;优点&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;免费&lt;/li&gt;
&lt;li&gt;麻省理工学院出品，有高等学院背书&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;缺点&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;在中国被封了&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;官网：&lt;a href="https://scratch.mit.edu/" rel="nofollow" target="_blank" title=""&gt;https://scratch.mit.edu/&lt;/a&gt;&lt;/p&gt;
&lt;h2 id="参考资料"&gt;参考资料&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://stackoverflow.blog/2021/01/12/want-to-teach-your-kids-to-code-here-are-three-apps-that-can-help/" rel="nofollow" target="_blank" title=""&gt;Stack Overflow Blog: Want to teach your kids to code? Here are three apps that can help.&lt;/a&gt;&lt;/p&gt;</description>
      <author>xiaoronglv</author>
      <pubDate>Thu, 15 Sep 2022 18:57:14 +0800</pubDate>
      <link>https://ruby-china.org/topics/42654</link>
      <guid>https://ruby-china.org/topics/42654</guid>
    </item>
    <item>
      <title>API 设计：你们是用 UUID 还是 HEX?</title>
      <description>&lt;p&gt;通常大家设计 Restful API 时，会直接返回资源的数字 id 给前端。比如以下一个关于账单的 API，返回当前绑定的信用卡，它的资源 id 是一个数字。&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;GET /v1/credit_cards.json

{
  id: 13232,
  number: "61333313333000",
 ...
}

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;为了安全，大家都会加上过滤条件 &lt;code&gt;where(company_id: current_company.id)&lt;/code&gt; 。&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;CreditCardsController
  def index
    render CreditCard.credit_cards.where(company_id: current_company.id)
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这样公司 A 的员工就不能访问公司 B 的数据了。但是如果万一有程序员忘记加 &lt;code&gt;where(company_id: current_company.id)&lt;/code&gt; 这个 filter，就会导致数据泄露。&lt;/p&gt;

&lt;p&gt;我看到有些公司的做法是，返回十六进制的 id。&lt;/p&gt;

&lt;p&gt;比如 checkr&lt;/p&gt;

&lt;p&gt;&lt;a href="https://docs.checkr.com/#operation/getCandidate" rel="nofollow" target="_blank"&gt;https://docs.checkr.com/#operation/getCandidate&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src="https://l.ruby-china.com/photo/xiaoronglv/88bf0efc-f523-4611-b670-34377d50d575.png!large" title="" alt=""&gt;&lt;/p&gt;
&lt;h2 id="第一个问题：为什么大家不在API中用 uuid 呢？"&gt;第一个问题：为什么大家不在 API 中用 uuid 呢？&lt;/h2&gt;
&lt;p&gt;创建方法&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;SecureRandom.uuid
&lt;/code&gt;&lt;/pre&gt;
&lt;ol&gt;
&lt;li&gt;如果用 uuid，黑客更不容易拼出完整 URL。&lt;/li&gt;
&lt;li&gt;万一某个 API 有安全漏洞，也不容易导致大规模的数据泄露。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;比如 API response 范例&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
{
  uuid: "6c8aa96e-4293-408c-baf7-01980faec5bc",
  number: "61333313333000",

}
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id="第二个问题：大家在设计企业应用时，返回给前端（合作伙伴）的是 uuid 还是十六进制的id？"&gt;第二个问题：大家在设计企业应用时，返回给前端（合作伙伴）的是 uuid 还是十六进制的 id？&lt;/h2&gt;
&lt;p&gt;感觉十六进制的 id 还是比较容易猜的。&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;SecureRandom.hex
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id="第三个问题：大家在做集成时，uuid 和 hex，哪个更常见？"&gt;第三个问题：大家在做集成时，uuid 和 hex，哪个更常见？&lt;/h2&gt;</description>
      <author>xiaoronglv</author>
      <pubDate>Mon, 29 Aug 2022 19:15:23 +0800</pubDate>
      <link>https://ruby-china.org/topics/42617</link>
      <guid>https://ruby-china.org/topics/42617</guid>
    </item>
    <item>
      <title>Rails 应用的部署管理</title>
      <description>&lt;p&gt;当使用命令行 &lt;code&gt;rails new&lt;/code&gt; 去创建一个新项目时，Rails 默认会创建三个环境：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;development: 本地开发环境。&lt;/li&gt;
&lt;li&gt;test: 用来跑测试。&lt;/li&gt;
&lt;li&gt;production: 生产环境，用来服务客户。&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;在项目的最早期，还处于验证想法阶段。工程师的工作流程也比较粗狂，写代码，跑测试，然后直接部署代码至生产环境。&lt;/p&gt;
&lt;h2 id="业务的第一阶段：少量客户阶段"&gt;业务的第一阶段：少量客户阶段&lt;/h2&gt;
&lt;p&gt;产品不断迭代，逐渐的有客户开始使用产品。为了保证工程质量，这时候需要给 Rails 添加一个预发布环境，通常大家都命名为 staging 环境，工作流程也开始严谨：&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;工程师在本地开发新功能。（developer 环境）&lt;/li&gt;
&lt;li&gt;在 CI（或本地）跑测试。（test 环境）&lt;/li&gt;
&lt;li&gt;代码合并到 staging 分支，并且部署到 staging 环境，QA 工程师测试新功能。&lt;/li&gt;
&lt;li&gt;如果没有 bug，则部署到 production 环境，供客户使用。&lt;/li&gt;
&lt;/ol&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;environments
├── development.rb
├── staging.rb
├── production.rb
└── test.rb
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id="业务的第二阶段：大量客户 + 多团队协作"&gt;业务的第二阶段：大量客户 + 多团队协作&lt;/h2&gt;
&lt;p&gt;如果产品市场反响良好，客户越来越多。工程师团队规模也由几个人扩增到几十人或上百人。人员众多，且共用一个 staging 环境，合并代码到 staging 分支时容易冲突。&lt;/p&gt;

&lt;p&gt;程序员耐心时，遇到代码冲突会联系原作者，一起解决。如果是跨国团队，因为时差的原因，一个来回的沟通需要一天时间。这时候程序员可能就没了耐心，直接重置 git 的 staging 分支。这会导致一系列的误会，别人正在测试的功能凭空消失，QA 工程师上报 bug，结果细查下来是代码重置导致的。&lt;/p&gt;

&lt;p&gt;为了降低代码冲突和沟通成本，很多公司会为各团队创建专属的部署环境，而不是吃大锅饭。各团队井水不犯河水，如果某个部署环境出现问题，也只会影响到单个团队，不会拖累整个公司开发进度。&lt;/p&gt;
&lt;table class="table table-bordered table-striped"&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;th style="text-align:left;"&gt;实例&lt;/th&gt;
&lt;th style="text-align:left;"&gt;部署的 Git 分支&lt;/th&gt;
&lt;th style="text-align:left;"&gt;用途&lt;/th&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style="text-align:left;"&gt;production&lt;/td&gt;
&lt;td style="text-align:left;"&gt;release/xxx&lt;/td&gt;
&lt;td style="text-align:left;"&gt;给客户使用的生产环境&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style="text-align:left;"&gt;demo&lt;/td&gt;
&lt;td style="text-align:left;"&gt;demo&lt;/td&gt;
&lt;td style="text-align:left;"&gt;销售给客户做演示。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style="text-align:left;"&gt;staging&lt;/td&gt;
&lt;td style="text-align:left;"&gt;main / 下一个 release 分支&lt;/td&gt;
&lt;td style="text-align:left;"&gt;用于上线前的回归测试&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style="text-align:left;"&gt;staging-1&lt;/td&gt;
&lt;td style="text-align:left;"&gt;staging-1-branch&lt;/td&gt;
&lt;td style="text-align:left;"&gt;给 "小熊猫" 团队使用&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style="text-align:left;"&gt;staging-2&lt;/td&gt;
&lt;td style="text-align:left;"&gt;staging-2-branch&lt;/td&gt;
&lt;td style="text-align:left;"&gt;给 "自杀小分队" 团队使用&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style="text-align:left;"&gt;staging-3&lt;/td&gt;
&lt;td style="text-align:left;"&gt;staging-3-branch&lt;/td&gt;
&lt;td style="text-align:left;"&gt;给 "星河战舰" 团队使用&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style="text-align:left;"&gt;staging-4&lt;/td&gt;
&lt;td style="text-align:left;"&gt;staging-4-branch&lt;/td&gt;
&lt;td style="text-align:left;"&gt;给 "深海水妖" 团队使用&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style="text-align:left;"&gt;staging-...&lt;/td&gt;
&lt;td style="text-align:left;"&gt;staging-6-branch&lt;/td&gt;
&lt;td style="text-align:left;"&gt;给 ... 团队使用&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;工程师越多，需要的 Rails 部署环境也越多。如果我们要创建 6 个部署环境 staging，staging-1, staging-2, staging-3, staging-4, demo, 该怎么做呢？&lt;/p&gt;

&lt;p&gt;可以在 Rails 的 &lt;code&gt;config/environments&lt;/code&gt; 目录下创建 6 个配置文件。这是 Rails 推荐的配置风格，原汁原味。&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;gt; tree environments                                                      
environments
├── demo.rb
├── development.rb
├── test.rb
├── production.rb
├── staging.rb
├── staging-1.rb
├── staging-2.rb
├── staging-3.rb
├── staging-4.rb
...
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;但事情没那么简单，除了 &lt;code&gt;config/environments&lt;/code&gt;，还有许多额外工作。我们要在 &lt;code&gt;config/database.yml&lt;/code&gt; 添加 6 个新部署环境的数据库配置。&lt;/p&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;default&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nl"&gt;&amp;amp;default&lt;/span&gt;
  &lt;span class="na"&gt;adapter&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;postgresql&lt;/span&gt;
  &lt;span class="na"&gt;database&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;lt;%= ENV['DATABASE_NAME'] %&amp;gt;&lt;/span&gt;
  &lt;span class="na"&gt;username&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;lt;%= ENV.fetch("DATABASE_USER", 'postgres') %&amp;gt;&lt;/span&gt;
  &lt;span class="na"&gt;password&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;lt;%= ENV['DATABASE_PASSWORD'] %&amp;gt;&lt;/span&gt;
  &lt;span class="na"&gt;port&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;5432&lt;/span&gt;

&lt;span class="na"&gt;production&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;*default&lt;/span&gt;

&lt;span class="na"&gt;demo&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;*default&lt;/span&gt;

&lt;span class="na"&gt;staging-1&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;*default&lt;/span&gt;

&lt;span class="na"&gt;staging-2&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;*default&lt;/span&gt;

&lt;span class="na"&gt;staging-3&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;*default&lt;/span&gt;
&lt;span class="nn"&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;此外，如果有些工程师在常量中定义配置，那我们还需要全局搜索 &lt;code&gt;Rails.env&lt;/code&gt;，确保新创建的部署环境，都有匹配的赋值。尤其是在犄角旮旯里定义的变量，如果遗漏，新部署会一直报错。&lt;/p&gt;

&lt;p&gt;比如，有人会把各个部署环境的 Redis 地址写在了常量中。&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# config/initializers/sidekiq.rb

case Rails.env
when :demo
  REDIS_HOST = 'redis-demo.3922002.redis.aws.com:6379'
when :staging-1
  REDIS_HOST = 'redis-s1.3922002.redis.aws.com:6379'
when :staging-2
  REDIS_HOST = 'redis-s2.3922002.redis.aws.com:6379'
...
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;在常量里定义配置，太容易出错。多数工程师会把不同部署环境的配置抽出来，放到不同的配置文件中，并且用一些库来管理，Rails 常用的库是 &lt;a href="https://github.com/rubyconfig/config" rel="nofollow" target="_blank" title=""&gt;rubyconfig/config&lt;/a&gt;。如果团队在使用这些库，我们也需要为新的部署环境添加配置文件。&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;config/settings/demo.yml
config/settings/production.yml
config/settings/staging.yml
config/settings/staging-1.yml
config/settings/staging-2.yml
config/settings/staging-3.yml
config/settings/staging-4.yml
...
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;以上方案有几个的缺点：&lt;/p&gt;

&lt;p&gt;第一，即使有完备的内部文档，全套走下来，工程量不小，人力成本很高。&lt;/p&gt;

&lt;p&gt;第二，每次创建新的部署环境都要修改老代码中的常量。修改正在运行的老代码乃兵家大忌，稍有不慎就会 bug 缠身，死无葬身之地。&lt;/p&gt;

&lt;p&gt;第三，安全性差。配置中包含 client_secret，token，私钥，如果代码不幸泄露，各种秘钥被一锅端。使用这种方案创建的 docker 镜像混杂了各种配置信息，不干净。万一泄露，也是个麻烦事。&lt;/p&gt;

&lt;p&gt;虽然有种种缺陷，但这种 Rails 原生的解决方案也能维持一段时间。&lt;/p&gt;
&lt;h2 id="业务的第三阶段：多租户方案 + 私有化部署 + 百人以上开发团队"&gt;业务的第三阶段：多租户方案 + 私有化部署 + 百人以上开发团队&lt;/h2&gt;
&lt;p&gt;项目早期，通常只有一个 production 部署环境。&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;如果产品面向的是普通客户（To C），一个 production 部署就能满足所有客户的需要。&lt;/li&gt;
&lt;li&gt;面向企业客户的 SaaS 产品，早期也是多租户（multi-tenant）设计，一个 production 部署环境服务所有的企业。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;因此很多程序员脑子里有个假设："production 部署环境只有一个"。随着业务的增长，这种假设会被打破。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;为了国家安全，一些国家要求本国的公民的数据必须保存在本国的数据中心&lt;/strong&gt;。Apple iCloud 既在美国有数据中心，也在中国贵州有数据中心，同一套代码部署在中国和美国，都是以 production 模式运行。抖音在美国部署在 Oracle Cloud 上，在中国则部署在自己的机房里，他们也都以 production 模式运行。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;大客户处于安全的考虑，要求私有化部署&lt;/strong&gt;。阿里云同样一套代码，会卖给政府，中国电信，公安系统，代码部署在他们各自的机房。Gitlab 是一个开源的代码管理平台，客户可以选择使用云版本，也可以私有化部署到内网，无数企业为了安全性选择后者。所有企业的运行模式也都为 production。&lt;/p&gt;

&lt;p&gt;所以一套 SaaS，在真实的世界中的部署场景应该是这个样子：&lt;/p&gt;
&lt;table class="table table-bordered table-striped"&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;th style="text-align:left;"&gt;运行实例&lt;/th&gt;
&lt;th style="text-align:left;"&gt;部署的 Git 分支&lt;/th&gt;
&lt;th style="text-align:left;"&gt;用途&lt;/th&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style="text-align:left;"&gt;production&lt;/td&gt;
&lt;td style="text-align:left;"&gt;release/100*&lt;/td&gt;
&lt;td style="text-align:left;"&gt;部署在公有云上，多租户使用的 SaaS&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style="text-align:left;"&gt;production-gov&lt;/td&gt;
&lt;td style="text-align:left;"&gt;release/101&lt;/td&gt;
&lt;td style="text-align:left;"&gt;客户为政府，部署在政府私有机房&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style="text-align:left;"&gt;production-cnpc&lt;/td&gt;
&lt;td style="text-align:left;"&gt;release/99&lt;/td&gt;
&lt;td style="text-align:left;"&gt;客户为中石油，部署在中石油私有机房&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style="text-align:left;"&gt;production-china-police&lt;/td&gt;
&lt;td style="text-align:left;"&gt;release/100&lt;/td&gt;
&lt;td style="text-align:left;"&gt;客户为中国警察，部署在公安私有机房&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style="text-align:left;"&gt;production-us-police&lt;/td&gt;
&lt;td style="text-align:left;"&gt;release/100&lt;/td&gt;
&lt;td style="text-align:left;"&gt;客户为美国警察，部署在 AWS&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style="text-align:left;"&gt;production-huawei&lt;/td&gt;
&lt;td style="text-align:left;"&gt;release/100&lt;/td&gt;
&lt;td style="text-align:left;"&gt;客户为华为，私有化部署&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style="text-align:left;"&gt;demo&lt;/td&gt;
&lt;td style="text-align:left;"&gt;demo&lt;/td&gt;
&lt;td style="text-align:left;"&gt;销售给客户做演示。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style="text-align:left;"&gt;staging&lt;/td&gt;
&lt;td style="text-align:left;"&gt;main / 下一个 release 分支&lt;/td&gt;
&lt;td style="text-align:left;"&gt;用于上线前的回归测试&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style="text-align:left;"&gt;staging-1&lt;/td&gt;
&lt;td style="text-align:left;"&gt;staging-1 branch&lt;/td&gt;
&lt;td style="text-align:left;"&gt;给 "小熊猫" 团队开发测试使用&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style="text-align:left;"&gt;staging-2&lt;/td&gt;
&lt;td style="text-align:left;"&gt;staging-2 branch&lt;/td&gt;
&lt;td style="text-align:left;"&gt;给 "自杀小分队" 团队开发测试使用&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style="text-align:left;"&gt;staging-3&lt;/td&gt;
&lt;td style="text-align:left;"&gt;staging-3 branch&lt;/td&gt;
&lt;td style="text-align:left;"&gt;给 "星河战舰" 团队开发测试使用&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style="text-align:left;"&gt;staging-4&lt;/td&gt;
&lt;td style="text-align:left;"&gt;staging-4 branch&lt;/td&gt;
&lt;td style="text-align:left;"&gt;给 "深海水妖" 团队开发测试使用&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style="text-align:left;"&gt;staging-...&lt;/td&gt;
&lt;td style="text-align:left;"&gt;staging-... branch&lt;/td&gt;
&lt;td style="text-align:left;"&gt;给 ... 团队开发测试使用&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;blockquote&gt;
&lt;p&gt;note:&lt;/p&gt;

&lt;p&gt;release/xxx 代表某个已经过 QA 测试的部署分支。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;当有众多 production 环境时，难道要把所有客户的配置都混入到代码里吗？显然不可能！客户也不答应！所以大家不约而同的认为代码应该无状态，配置信息应单独存储，这样创建一个部署环境时就不需要改动一行代码了。&lt;/p&gt;

&lt;p&gt;&lt;img src="https://mednoter.com/media/files/2022/2022-08-12-codebase-deploys.png" title="" alt=""&gt;&lt;/p&gt;
&lt;h2 id="概念：部署 (deploy)"&gt;概念：部署 (deploy)&lt;/h2&gt;
&lt;p&gt;当一套代码的 production 环境只有一个时，我们可以用 "production "等字眼代指某个运行的实例。&lt;/p&gt;

&lt;p&gt;当一套 Gitlab 代码有成千上万个 production 运行实例时，"production 环境"就无法指代任何事。&lt;/p&gt;

&lt;p&gt;为了更精确的表达，&lt;strong&gt;我们用部署（deploy）这个概念代指代码的运行实例&lt;/strong&gt;。&lt;/p&gt;

&lt;p&gt;同时 &lt;code&gt;config/environments&lt;/code&gt; 下的个文件 production.rb / staging.rb / development.rb / test.rb，我们称之为&lt;strong&gt;代码的运行模式&lt;/strong&gt;。&lt;/p&gt;

&lt;p&gt;当 Gitlab 部署到华为内网时，我们可以这样描述 "Gitlab 在华为创建了一个部署（deploy），它的运行模式为 production"。&lt;/p&gt;
&lt;h2 id="The 12-factor Application"&gt;The 12-factor Application&lt;/h2&gt;
&lt;p&gt;Heroku 写过一篇 SaaS 架构文章《&lt;a href="https://12factor.net/" rel="nofollow" target="_blank" title=""&gt;12-factor  Application&lt;/a&gt;》，概括了设计 SaaS 应用的 12 条原则，被奉为圭臬。时至今日，依然无出其右。&lt;/p&gt;
&lt;h3 id="第一条原则：一份基准代码（Codebase），多份部署（deploy）"&gt;第一条原则：一份基准代码（Codebase），多份部署（deploy）&lt;/h3&gt;
&lt;p&gt;每个应用的一份基准代码，可以同时存在多份部署。每份部署相当于运行了一个应用的实例。通常会有一个生产环境，一个或多个预发布环境。&lt;/p&gt;
&lt;h3 id="第二条原则：显式声明依赖关系（ dependency ）"&gt;第二条原则：显式声明依赖关系（dependency）&lt;/h3&gt;
&lt;p&gt;同样的代码，在不同机器，依赖要一致，行为也要一致，比如：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;JavaScript 使用 npm 或 yarn 来管理各种库的依赖。&lt;/li&gt;
&lt;li&gt;Ruby 使用 Bundler 来管理各种库的版本依赖。&lt;/li&gt;
&lt;li&gt;Docker 不但打包代码的库的依赖，还打包了操作系统的依赖。任何人获得 docker image 后都可以运行代码，不会出现这种尴尬情况：一份代码只能在我的电脑上跑，不能在你的电脑上跑。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="第三条原则：在环境中存储配置"&gt;第三条原则：在环境中存储配置&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;a href="https://12factor.net/zh_cn/config" rel="nofollow" target="_blank" title=""&gt;The 12-factor App: 配置&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;12-Factor 推荐将应用的配置存储于环境变量中（env vars, env）。环境变量可以非常方便地在不同的部署间做修改，却不动一行代码。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;Docker&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;比如 Docker 允许你把配置放到环境变量中，从而创建不同的实例（container）。&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;docker run --name postgresql \
  -e POSTGRES_USER=myusername \
  -e POSTGRES_PASSWORD=mypassword \
  -p 5432:5432 -v /data:/var/lib/postgresql/data \
  -d postgres
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Helm&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://helm.sh/" rel="nofollow" target="_blank" title=""&gt;Helm&lt;/a&gt; 是一个 Kubernetes 应用的包管理工具，它也遵循了 12-factor 的前三条原则。它有三个核心概念 chart，config，release。&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;chart 是模版，无状态。&lt;/li&gt;
&lt;li&gt;config 是配置信息&lt;/li&gt;
&lt;li&gt;release = chart + config。当把模版和配置拼凑在一起时，就创建了一个部署实例。&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;我想开源软件之所以更偏爱 12-factor，是因为作者从第一天开始就知道，这份软件会千千万万个个体使用，所以绝对不能 hard code，配置和代码必须分离。&lt;/p&gt;

&lt;p&gt;我们可以借鉴 12-factor 思想，用一份代码代码可以迅速创建几十个部署，让业务程序员开心，让 DevOps 开心，让客户开心。&lt;/p&gt;
&lt;h2 id="优化方案"&gt;优化方案&lt;/h2&gt;
&lt;p&gt;第一步，让代码无状态，代码中所有的配置信息都取自环境变量。&lt;/p&gt;

&lt;p&gt;比如，DB 的配置要取自环境变量。&lt;/p&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# config/database.yml&lt;/span&gt;
&lt;span class="c1"&gt;# ...&lt;/span&gt;
&lt;span class="na"&gt;production&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;*default&lt;/span&gt;
  &lt;span class="na"&gt;database&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;lt;%= ENV['DATABASE_NAME' %&amp;gt;&lt;/span&gt;
  &lt;span class="na"&gt;host&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;lt;%= ENV['DATABASE_HOST'] %&amp;gt;&lt;/span&gt;
  &lt;span class="na"&gt;password&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;lt;%= ENV['DATABASE_PASSWORD'] %&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Sidekiq 的配置也取自环境变量。&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="no"&gt;Sidekiq&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;configure_server&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
  &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;redis&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="ss"&gt;host: &lt;/span&gt;&lt;span class="no"&gt;ENV&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'REDIS_HOST'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="ss"&gt;port: &lt;/span&gt;&lt;span class="mi"&gt;6379&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="c1"&gt;# ...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;任何逻辑，但凡和部署相关，其配置都取自环境变量。&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;OauthController&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ApiController&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;redirect&lt;/span&gt;
    &lt;span class="n"&gt;redirect_to&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;ENV&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'GOOGLE_OAUTH_URL'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;第二步，准备配置文件。&lt;/p&gt;

&lt;p&gt;为 staging-1, staging-2, staging-3, staging-4,demo，production，production-gov，production-china-police，production-us-police 等部署环境创建配置文件。&lt;/p&gt;

&lt;p&gt;如果你使用的是 AWS，可以把某个部署的配置保存在 &lt;a href="https://docs.aws.amazon.com/systems-manager/latest/userguide/systems-manager-parameter-store.html" rel="nofollow" target="_blank" title=""&gt;Parameter Store&lt;/a&gt;，敏感信息可以保存在 &lt;a href="https://aws.amazon.com/secrets-manager/" rel="nofollow" target="_blank" title=""&gt;AWS secret manager&lt;/a&gt;。&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# 创建 staging-1 的配置信息
aws ssm put-parameter \
    --name "staging-1-configuration" \
    --value "parameter-value" \
    --type String \
    --tags "DB_HOST=xxx,DB_USER=xxx,DB_PASSWORD=xxx,REDIS_HOST=xxx,GOOGLE_OAUTH_URL=xxxx"
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;如果你是用的是 Kubernetes，可以把不同部署的配置信息保存在不同的 &lt;a href="https://kubernetes.io/docs/concepts/configuration/configmap/" rel="nofollow" target="_blank" title=""&gt;ConfigMap&lt;/a&gt;，敏感信息保存在 &lt;a href="https://kubernetes.io/docs/concepts/configuration/secret/" rel="nofollow" target="_blank" title=""&gt;Secret&lt;/a&gt; 中。&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;kubectl create configmap staging1-config-map \
  --from-literral="DB_HOST=xxx" \
  --from-literral="DB_USER=postgres" \
  --from-literral="DB_PASSWORD=xxx"
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;第三步，以 production mode 运行各个部署环境。&lt;/p&gt;

&lt;p&gt;staging-1, staging-2, staging-3, staging-4, staging-5, ... staging-x，demo，production，production-gov，production-china-police，production-us-police 内部的运行脚本都如下所示：&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;export .env
RAILS_ENV=production rails s
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src="https://mednoter.com/media/files/2022/2022-08-12_14-07-10-production-yml.jpg" title="" alt=""&gt;&lt;/p&gt;

&lt;p&gt;第四步：把代码和配置组合在一起，创建一个个部署。&lt;/p&gt;

&lt;p&gt;&lt;img src="https://mednoter.com/media/files/2022/2022-08-12-codebase-build-config-deploy.png" title="" alt=""&gt;&lt;/p&gt;

&lt;p&gt;如果你使用的是 Capistrano 部署代码，那么&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Code + 配置 = 部署
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;如果你使用的是 Docker 部署代码，那么：&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Docker Image + 配置 = 部署
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;如果你使用的是 Kubenetes，将不同配置文件注入到了 Pod 中，就变成了不同的部署。&lt;/p&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="nn"&gt;---&lt;/span&gt;
&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;v1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Pod&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;labels&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;webapp&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;webapp&lt;/span&gt;
  &lt;span class="na"&gt;namespace&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;default&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;containers&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;web-app&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;web-app:staging-1&lt;/span&gt;
    &lt;span class="na"&gt;envFrom&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;configMapRef&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;  &lt;span class="s"&gt;👈 看这里&lt;/span&gt;
        &lt;span class="s"&gt;name&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="s"&gt;staging-1-config-map&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;secretRef&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;     &lt;span class="s"&gt;👈 看这里&lt;/span&gt;
        &lt;span class="s"&gt;name&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="s"&gt;staging-1-secrets&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id="优点"&gt;优点&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;&lt;p&gt;代码无状态，可以被复用。如果想创建部署，只需要准备一份新的配置文件就可以，省事省力。&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;代码和 Docker image 中不包含机密信息。即使代码泄露也不会扩大风险。&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;方便私有化部署。&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;可以为不同部署的配置文件可以设置不同的访问权限，比如仅仅允许特定团队访问 production 部署实例的的配置信息。&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;在本文中所有部署的运行模式都是 production mode，以此消除了不同部署的差异 (&lt;a href="https://12factor.net/dev-prod-parity" rel="nofollow" target="_blank" title=""&gt;parity&lt;/a&gt;)。&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id="缺点"&gt;缺点&lt;/h3&gt;
&lt;p&gt;NewRelic, Datadog, Sentry 等监控工具默认会把环境信息附着在监控数据上，方便筛选过滤。在第二种方案中，所有的部署的运行模式都为 production mode，这导致监控数据都混在 production 下。遇到事故，工程师根本无法排查是哪个部署出了问题。&lt;/p&gt;

&lt;p&gt;&lt;img src="https://mednoter.com/media/files/2022/2022-08-12_12-56-12-only-production.jpg" title="" alt=""&gt;&lt;/p&gt;
&lt;h3 id="如何解决监控工具的问题？"&gt;如何解决监控工具的问题？&lt;/h3&gt;
&lt;p&gt;其实 NewRelic, Datadog, Sentry 提供了接口，允许我们自定义部署的名字，以 Sentry 举例，它的语法如下：&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="no"&gt;Sentry&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;init&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
  &lt;span class="c1"&gt;#...&lt;/span&gt;
  &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;environment&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"你喜欢的部署名"&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;因此我们可以在 staging-1, staging-2, staging-x, production-x 等不同部署的配置中，引入一个新的变量 "DEPLOY_ID" 来声明部署的名称，并赋值给监控工具。&lt;/p&gt;

&lt;p&gt;假如 staging-1 的配置信息如下：&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;DEPLOY_ID =staging-1
DB_HOST=xxx
DB_USER=xxx
...
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;我们可以这样配置 Sentry：&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="no"&gt;Sentry&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;init&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
  &lt;span class="c1"&gt;#...&lt;/span&gt;
  &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;environment&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;ENV&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'DEPLOY_ID'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="c1"&gt;# 它的值为 staging-1&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;可以这样配置 Datadog：&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="no"&gt;Datadog&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;configure&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
  &lt;span class="c1"&gt;# ...&lt;/span&gt;
  &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;env&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;ENV&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'DEPLOY_ID'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="c1"&gt;# 它的值为 staging-1&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这样就能保证监控工具的正常显示了。&lt;/p&gt;

&lt;p&gt;&lt;img src="https://mednoter.com/media/files/2022/2022-08-12_20-35-02-deploy.jpg" title="" alt=""&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;感谢&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;本文中的方案，来自于 Workstream 同事们 和 SAP 前同事们的实践经验，我只是加工整理，提笔记录下来。&lt;/p&gt;

&lt;p&gt;特别感谢 &lt;a href="https://www.linkedin.com/in/louise-x-87125b227/" rel="nofollow" target="_blank" title=""&gt;Louise Xu&lt;/a&gt;, Felix Chen, &lt;a href="https://www.linkedin.com/in/vincent-huang-13111068/" rel="nofollow" target="_blank" title=""&gt;Vincent Huang&lt;/a&gt;, &lt;a href="https://www.linkedin.com/in/teddy-wang-b4191549/" rel="nofollow" target="_blank" title=""&gt;Teddy Wang&lt;/a&gt;, Kang Zhang 的审校和反馈。&lt;/p&gt;</description>
      <author>xiaoronglv</author>
      <pubDate>Fri, 12 Aug 2022 21:57:43 +0800</pubDate>
      <link>https://ruby-china.org/topics/42591</link>
      <guid>https://ruby-china.org/topics/42591</guid>
    </item>
    <item>
      <title>提高中国程序员幸福感的网络调试工具</title>
      <description>&lt;blockquote&gt;
&lt;p&gt;本文是一个纯技术帖，目的是为了分享“如何更高效的配置开发环境，以及更开心的敲代码”，无意把话题引上那个不该讨论的内容。所以我把评论关了，免得给社区惹麻烦。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id="前言"&gt;前言&lt;/h2&gt;
&lt;p&gt;在国内配置开发环境是一种折磨。&lt;/p&gt;

&lt;p&gt;yarn install 慢；npm install 慢；bundle install 慢。为了提高下载速度，必须把各种依赖的下载地址替换为国内镜像，或者使用 HTTP Proxy。&lt;/p&gt;

&lt;p&gt;git clone 慢，需要设置环境变量，指定 HTTP Proxy。&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ export http_proxy=http://127.0.0.1:8087
$ export https_proxy=http://127.0.0.1:8087
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;SSH 慢，需要在  .ssh/config 文件中配置 HTTP Proxy。&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Host github.com
    User git
    ProxyCommand ProxyCommand ncat --proxy 127.0.0.1:1080
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;访问 Stack Overflow 慢，访问 Github 特别慢，需要给 Chrome 浏览器下载一个插件 &lt;a href="https://chrome.google.com/webstore/detail/proxy-switchyomega/padekgcemlokbadohgkifijomclgjgif?hl=en" rel="nofollow" target="_blank" title=""&gt;Proxy SwitchyOmega&lt;/a&gt; 管理 Proxy，根据不同的域名选择不同的线路，提高访问速度。&lt;/p&gt;

&lt;p&gt;以上是大部分程序员的三板斧。&lt;/p&gt;

&lt;p&gt;但它有诸多不便，每次切换 Proxy 的线路，或者开关 Proxy，都要为各软件逐个修改配置，terminal，chrome，ssh config，有人甚至还写 shell 脚本来批量操作。&lt;/p&gt;

&lt;p&gt;也有人为整个操作系统设置一个默认代理，这样就不必为每个软件单独设置了。但是这会导致国内外的流量都走代理，访问国内的网站也要绕地球一圈。国外网站变快了，国内网站变慢了。&lt;/p&gt;

&lt;p&gt;此外 HTTP Proxy 工作在 OSI 网络模型的第七层"应用层"，并不是所有的软件都支持。即使你设置了系统代理，有些小众的软件可能不理睬这个设置。&lt;/p&gt;

&lt;p&gt;这些问题耗费了程序员大量的时间。&lt;/p&gt;
&lt;h2 id="Surge"&gt;Surge&lt;/h2&gt;
&lt;p&gt;如果你使用的是 MacOS，我建议你直接购买 &lt;a href="https://nssurge.com/buy_now" rel="nofollow" target="_blank" title=""&gt;Surge&lt;/a&gt; 这款网络调试工具。&lt;/p&gt;

&lt;p&gt;它的最大亮点是“&lt;a href="https://surge.mitsea.com/others/enhanced-mode" rel="nofollow" target="_blank" title=""&gt;增强模式&lt;/a&gt;”，它会安装一个虚拟网卡并设置为默认路由，根据规则自动分发流量。这相当于在 OSI 的底层就分流了，第七层应用层的软件根本察觉不到 Proxy 的存在。&lt;/p&gt;

&lt;p&gt;重要的事情说三遍：&lt;/p&gt;

&lt;p&gt;如果你购买了 Surge，请务必打开增强模式 (enhanced mode)，否则暴殄天物。&lt;/p&gt;

&lt;p&gt;如果你购买了 Surge，请务必打开增强模式 (enhanced mode)，否则暴殄天物。&lt;/p&gt;

&lt;p&gt;如果你购买了 Surge，请务必打开增强模式 (enhanced mode)，否则暴殄天物。&lt;/p&gt;

&lt;p&gt;&lt;img src="https://mednoter.com/media/files/2022/2022-04-18-surge.jpg" title="" alt=""&gt;&lt;/p&gt;

&lt;p&gt;也许有人有人会推荐其他免费开源的网络调试工具，我就问一个问题：它在网络模型中，工作在第几层？如果是 HTTP Proxy，就不要谈了，因为它无法解决前言中提到的各种缺陷。&lt;/p&gt;

&lt;p&gt;此外 Surge 的功能并不仅限于此，它还可以对 HTTPs 进行抓包，方便调试 API。要知道单独购买一个网络调试工具 Charles Web Debugging Proxy 就要花费 50 美金啊，而买一个 Surge，就可以有两份用途，简直赚翻了。&lt;/p&gt;
&lt;h2 id="Proxy Server"&gt;Proxy Server&lt;/h2&gt;
&lt;p&gt;Surge 是个单纯的软件，它只负责把网络包丢给服务器，所以你还需要一台 Proxy 服务器。但是不建议你自己搭，因为运维太浪费时间，时间就是金钱。推荐你购买这类服务商，也不贵，一年就几百块钱。&lt;/p&gt;
&lt;h2 id="行动"&gt;行动&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;购买 Surge，50 美金/终生授权，一次购买，终生享受。多花点时间敲代码不好吗，何必天天瞎折腾？&lt;/li&gt;
&lt;li&gt;购买 Proxy Servers，100-500 RMB/年。凭着你的聪明智慧，肯定可以找到合适的。&lt;br&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="参考"&gt;参考&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;点击购买 &lt;a href="https://nssurge.com/buy_now" rel="nofollow" target="_blank" title=""&gt;Surge for Mac&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;点击购买 &lt;a href="https://nssurge.com/buy_now" rel="nofollow" target="_blank" title=""&gt;Surge for iOS&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Surge 也有企业版，如果你是一家软件公司，可以购买企业版，大幅提高员工的生产力。点击购买 &lt;a href="https://enterprise.nssurge.com/" rel="nofollow" target="_blank" title=""&gt;Surge 企业版&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="https://stackoverflow.com/a/19213999/5117552" rel="nofollow" target="_blank" title=""&gt;Stack Overflow: Getting Git to work with a proxy server&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://stackoverflow.com/questions/19161960/connect-with-ssh-through-a-proxy" rel="nofollow" target="_blank" title=""&gt;Stack Overflow: Connect with SSH through a proxy&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://mednoter.com/slowness-of-github.html" rel="nofollow" target="_blank" title=""&gt;吕小荣：提高 Github 的访问速度&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;</description>
      <author>xiaoronglv</author>
      <pubDate>Sat, 30 Jul 2022 12:19:15 +0800</pubDate>
      <link>https://ruby-china.org/topics/42565</link>
      <guid>https://ruby-china.org/topics/42565</guid>
    </item>
    <item>
      <title>作为一个程序员，我在给孩子用的教育软件</title>
      <description>&lt;p&gt;吃自己的狗粮，作为一个程序员，我一直在用软件教孩子学习汉字，英语，编程，钢琴。7 分靠软件，3 分加陪伴。&lt;/p&gt;

&lt;p&gt;我有个朋友是老师，他说软件没有情感，无法替代老师，孩子无法学习老师的品格和做事方式。即使有这种先天性的缺陷，我仍坚信软件会吞噬教育领域，是寻常百姓更好的选择。&lt;/p&gt;

&lt;p&gt;有钱人拥有最好的教育资源，上最好的学区，家里有英语仆人，每年寒暑假出国旅行，沉浸在英语环境中，通过骑马滑雪等特长进入大学，也可以获得大师亲自指点。他们不需要教育软件，自有老师围着他们转。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;但软件让平民百姓以最低的价格接触到最优秀的老师。&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;在一线城市有大量外教老师和全英文学校，但是二三线城市没有那么好的条件。&lt;/p&gt;

&lt;p&gt;Khan academy（kids) 举例，它是一个 2 年级以下的教育软件。制作精良，内容涵盖英语，数学，逻辑。同样的学习时间，我坚信它的教育效率要甩出平庸的老师十万八千里。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;其次，软件把很多阳春白雪的领域拉下神坛，飞入寻常百姓家。&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;拿钢琴举例，上海一节钢琴课要 250-400 块钱，但是钢琴学习软件的终生授权才 2000 块钱。软件无法做到面对面的大师指导，但是他可以很快达到平均水平。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;软件 24 小时待命，孩子想学时拿过来就学，想读就读。&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;如果参加线下培训班，放学后家长和孩子东奔西跑，有一半时间花在路上。在教育上投资 3 个小时，其中 1.5 个小时是无效支出。如果使用教育软件，放学后孩子可以直接回家，在家里学习，同时多了更多的亲子时光。&lt;/p&gt;

&lt;p&gt;软件在吞噬世界，这是不可抗拒的潮流。但我们依然有选择，我们可以选择被好的内容吞噬，也可以在坏的潮流中随波逐流。互联网给人堕落机会，也给平民的孩子插上翅膀。平民孩子可以通过软件学习 MIT 的计算机课程，卡内基梅陇的数据库课程。&lt;/p&gt;
&lt;h3 id="Khan Academy Kids"&gt;Khan Academy Kids&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;费用：免费&lt;/li&gt;
&lt;li&gt;课程语言：中文、英文&lt;/li&gt;
&lt;li&gt;推荐星级：⭐⭐⭐⭐⭐&lt;br&gt;
&lt;/li&gt;
&lt;li&gt;易用性：小朋友可以自己操作，不需要大人指导，无广告。&lt;/li&gt;
&lt;li&gt;用途：学习英文，自然拼读，数学，自然科学&lt;/li&gt;
&lt;li&gt;适用年龄段：3-9 岁&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;这款软件内置五个可爱的卡通人物，一只大象，一只小熊，一只鸟，一只狐狸。在小伙伴的陪伴下带领孩子踏上了探索世界的旅程，孩子在通过一个个知识关卡的过程中，学习了数学，逻辑，英文自然拼读。软件是全英文界面，小朋友在学习的时候是完全沉浸式的。&lt;/p&gt;

&lt;p&gt;里面还提供了大量精美的英文绘本和视频，也可以晚上用来给孩子讲故事。&lt;/p&gt;

&lt;p&gt;Khan Academy Kids 完全可以满足小学 2 年级之前的教育，一个软件就够了！&lt;/p&gt;

&lt;p&gt;&lt;a href="https://learn.khanacademy.org/khan-academy-kids/" rel="nofollow" target="_blank" title=""&gt;&lt;img src="https://mednoter.com/media/files/2021/2021-02-17-khan-academy-kids.png" title="" alt=""&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3 id="Youtube Kids"&gt;Youtube Kids&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;费用：免费&lt;/li&gt;
&lt;li&gt;课程语言：英文&lt;/li&gt;
&lt;li&gt;推荐星级：⭐⭐⭐⭐⭐&lt;br&gt;
&lt;/li&gt;
&lt;li&gt;易用性：小朋友可以自己操作，不需要大人指导。偶尔片头有玩具的广告，不影响小朋友操作。&lt;/li&gt;
&lt;li&gt;用途：学习英文，磨耳朵。&lt;/li&gt;
&lt;li&gt;适用年龄段：3+ &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Youtube Kids 根据小朋友的年龄展示适合的动画片。当我的儿子 3 岁的时候，它推荐的都是英文儿歌。4 岁的时候，它推荐的是非常简单小汽车动画片，只有简单的句子。5 岁的时候，开始推荐有剧情的英文动画片。&lt;/p&gt;

&lt;p&gt;小朋友都喜欢看动画片，既然要看，不如练练英文听力，磨磨耳朵。&lt;/p&gt;

&lt;p&gt;此外动画片内容是审查过的，没有暴力和色情镜头，所以可以非常放心的让孩子一个人看，不用操心出现少儿不宜的内容。&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.youtube.com/intl/ALL_us/kids/" rel="nofollow" target="_blank" title=""&gt;&lt;img src="https://mednoter.com/media/files/2021/2021-02-17-youtube-kids.jpg" title="" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;如何安装 Youtube Kids？&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;购买一个美区 Apple Store 账户下载此 App。&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;fq。对于我们程序员，帮全家设置一个畅通的网络应该不是个大问题。    &lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id="洪恩识字"&gt;洪恩识字&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;推荐星级：⭐⭐⭐⭐&lt;/li&gt;
&lt;li&gt;课程语言：中文&lt;/li&gt;
&lt;li&gt;费用：有两种付费模式，订阅或者一次性买断。100-300 人民币&lt;br&gt;
&lt;/li&gt;
&lt;li&gt;易用性：小朋友可以自己操作，不需要大人指导。偶尔有弹出式营销广告，小朋友不知所以然。&lt;/li&gt;
&lt;li&gt;用途：识字。每个汉字都是一个游戏关卡，寓教于乐。&lt;/li&gt;
&lt;li&gt;适用年龄段：3+ &lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="Telling the time"&gt;Telling the time&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;推荐星级：⭐⭐⭐⭐&lt;/li&gt;
&lt;li&gt;费用：几块钱&lt;/li&gt;
&lt;li&gt;语言：英文&lt;/li&gt;
&lt;li&gt;用途：教孩子学习钟表。&lt;/li&gt;
&lt;li&gt;适用年龄段：5+ &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://apps.apple.com/us/app/interactive-telling-time/id482452233" rel="nofollow" target="_blank" title=""&gt;&lt;img src="https://mednoter.com/media/files/2022/2022-07-30-telling-the-time.webp" title="" alt=""&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3 id="Epic!"&gt;Epic!&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;推荐星级：⭐⭐⭐⭐⭐&lt;/li&gt;
&lt;li&gt;课程语言：英文&lt;/li&gt;
&lt;li&gt;费用：订阅制，79.99 美元/年。&lt;/li&gt;
&lt;li&gt;易用性：小朋友可以自己操作，不需要大人指导，无广告。&lt;/li&gt;
&lt;li&gt;用途：英文阅读&lt;/li&gt;
&lt;li&gt;适用年龄段：3+&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://www.getepic.com/" rel="nofollow" target="_blank" title=""&gt;&lt;img src="https://mednoter.com/media/files/2022/2022-07-30-epic.webp" title="" alt=""&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3 id="Sratch"&gt;Sratch&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;推荐星级：⭐⭐⭐⭐&lt;/li&gt;
&lt;li&gt;费用：免费&lt;/li&gt;
&lt;li&gt;课程语言：英文&lt;/li&gt;
&lt;li&gt;用途：教孩子学习编程。&lt;/li&gt;
&lt;li&gt;适用年龄段：7+ &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://scratch.mit.edu/" rel="nofollow" target="_blank" title=""&gt;&lt;img src="https://mednoter.com/media/files/2022/2022-07-30-scratch.png" title="" alt=""&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3 id="Farfaria"&gt;Farfaria&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;推荐星级：⭐⭐⭐ &lt;/li&gt;
&lt;li&gt;课程语言：英文&lt;/li&gt;
&lt;li&gt;费用：有两种付费模式，一，订阅。二，99 美元一次性买断。有时候它会搞活动，特惠 49 美元。 &lt;/li&gt;
&lt;li&gt;易用性：小朋友可以自己操作，不需要大人指导，无广告。&lt;/li&gt;
&lt;li&gt;用途：英文绘本&lt;/li&gt;
&lt;li&gt;适用年龄段：3+ &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://www.farfaria.com/" rel="nofollow" target="_blank"&gt;https://www.farfaria.com/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;如何付费？&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;登陆它的网站&lt;/li&gt;
&lt;li&gt;注册账户&lt;/li&gt;
&lt;li&gt;选择最后一项。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://www.farfaria.com/subscriptions" rel="nofollow" target="_blank" title=""&gt;&lt;img src="https://mednoter.com/media/files/2021/2021-02-17-farfaria.jpg" title="" alt=""&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3 id="洪恩数学"&gt;洪恩数学&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;推荐星级：⭐⭐⭐⭐&lt;/li&gt;
&lt;li&gt;课程语言：中文&lt;/li&gt;
&lt;li&gt;费用：有两种付费模式，订阅或者一次性买断。100-300 人民币&lt;br&gt;
&lt;/li&gt;
&lt;li&gt;易用性：小朋友可以自己操作，不需要大人指导，无广告。 &lt;/li&gt;
&lt;li&gt;用途：学习数学。比如数字，高矮，大小，长宽，加减运算。&lt;/li&gt;
&lt;li&gt;适用年龄段：3+ &lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="叽里呱啦"&gt;叽里呱啦&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;推荐星级：⭐⭐⭐⭐&lt;/li&gt;
&lt;li&gt;课程语言：中文&lt;/li&gt;
&lt;li&gt;费用：400 人民币左右&lt;/li&gt;
&lt;li&gt;易用性：小朋友可以自己操作，不需要大人指导。缺点是有弹出式大促广告，小朋友不知该怎么处理。&lt;/li&gt;
&lt;li&gt;用途：练习英文口语。&lt;/li&gt;
&lt;li&gt;适用年龄段：3+ &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Youtube kids / Khan Academy kids / Farfaria 上面介绍的几款英文软件，只是让孩子看视频，看书，并不能开口。叽里呱啦则是鼓励孩子开口，可以让孩子张口说英文。&lt;/p&gt;
&lt;h3 id="Flowkey"&gt;Flowkey&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;课程语言：英文&lt;/li&gt;
&lt;li&gt;费用：2000 人民币左右&lt;/li&gt;
&lt;li&gt;用途：练习钢琴。&lt;/li&gt;
&lt;li&gt;适用年龄段：5+&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;学钢琴的第一年，建议先跟着老师学一下指法，打好基础。后面就可以开始自学之路了。&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.flowkey.com/en" rel="nofollow" target="_blank" title=""&gt;&lt;img src="https://mednoter.com/media/files/2022/2022-07-30-flowkey.webp" title="" alt=""&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2 id="如何控制 iPad 使用时间？"&gt;如何控制 iPad 使用时间？&lt;/h2&gt;
&lt;p&gt;以上学习软件虽好，但是如果没日没夜的让孩子看 iPad，眼睛终归会出问题。我是这样帮助孩子控制电子设备的使用时间的。&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;购买一个 iPad，单独给孩子用。&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;在这个 iPad 上设置&lt;a href="https://support.apple.com/zh-cn/guide/ipad/ipadaf2aa9f2" rel="nofollow" target="_blank" title=""&gt;屏幕使用时间&lt;/a&gt;，每天只能使用 1 个小时的 iPad，超过时间自动锁定。&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Youtube kids 20 分钟/天&lt;/li&gt;
&lt;li&gt;Khan Academy kids + 洪恩识字 + 洪恩数学 + 叽里呱啦 + Farfaria  40 分钟/天&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;在限制孩子的电子设备之前，家长最好能以身作则，先控制好自己的屏幕时间。否则孩子会很困惑：一方面爸爸妈妈沉迷于手机，另一方面却霸道限制孩子，嘴里说的和行为不一致。&lt;/p&gt;
&lt;h2 id="增加户外活动时间，预防近视。"&gt;增加户外活动时间，预防近视。&lt;/h2&gt;
&lt;p&gt;目前科学界倾向于认为阳光可以促进儿童眼睛的发育，充足的户外运动可以帮助儿童预防近视。&lt;/p&gt;

&lt;p&gt;建议每天上 1 小时户外，下午 1 小时户外活动时间，对冲电子产品对眼睛的损伤。&lt;/p&gt;
&lt;h2 id="如何下载 美区 Apple Store 的 App？"&gt;如何下载 &lt;strong&gt;美区 Apple Store&lt;/strong&gt; 的 App？&lt;/h2&gt;
&lt;p&gt;Khan Academy Kids 和 Youtube Kids 虽好，但在中国区 Apple Store 并未上架。&lt;/p&gt;

&lt;p&gt;你可以在京东/淘宝/拼多多购买一个美国区的 Apple ID，在 iPad 上登陆 Apple Store 后就可以下载美区的软件了。&lt;/p&gt;
&lt;h2 id="结论"&gt;结论&lt;/h2&gt;
&lt;p&gt;如果你怕麻烦，只用 Khan Academy Kids，再加一个汉字学习软件就够了。工具不在多，贵在坚持。&lt;/p&gt;

&lt;p&gt;如果你乐于折腾 fq，又舍的花钱，那么你可以试一下 Youtube Kids。&lt;/p&gt;
&lt;h2 id="参考资料"&gt;参考资料&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://support.apple.com/zh-cn/guide/ipad/ipadaf2aa9f2/ipados" rel="nofollow" target="_blank" title=""&gt;iPad 使用手册：在 iPad 上为自己设置屏幕使用时间&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learn.khanacademy.org/khan-academy-kids/" rel="nofollow" target="_blank" title=""&gt;Khan Academy Kids&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.youtube.com/intl/ALL_us/kids/" rel="nofollow" target="_blank" title=""&gt;Youtube Kids&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.farfaria.com/" rel="nofollow" target="_blank" title=""&gt;Farfaria&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;声明：以上 App 和我都没有利益关系，也没有给我推广费。&lt;/p&gt;</description>
      <author>xiaoronglv</author>
      <pubDate>Sat, 30 Jul 2022 12:05:14 +0800</pubDate>
      <link>https://ruby-china.org/topics/42564</link>
      <guid>https://ruby-china.org/topics/42564</guid>
    </item>
    <item>
      <title>晒农具：分享一些有趣的效率软件</title>
      <description>&lt;p&gt;这是我每天干活经常用到的软件，分享一下。你们每天都用的效率软件有哪些？&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Anki，闪卡软件，每天学习。&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.alfredapp.com/" rel="nofollow" target="_blank" title=""&gt;Alfred&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://1password.com/" rel="nofollow" target="_blank" title=""&gt;1Password&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.google.com/search?q=Grammerly" rel="nofollow" target="_blank" title=""&gt;Grammarly&lt;/a&gt;，英文纠错，英文润色。&lt;/li&gt;
&lt;li&gt;一次买断的 &lt;a href="https://dayoneapp.com/" rel="nofollow" target="_blank" title=""&gt;Day One&lt;/a&gt;，写日记。现在这个软件已经不支持买断了，只能订阅，不推荐购买。&lt;/li&gt;
&lt;li&gt;Chrome + Vimium 插件&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.techsmith.com/screen-capture.html" rel="nofollow" target="_blank" title=""&gt;Snagit&lt;/a&gt;，截图工具&lt;/li&gt;
&lt;li&gt;iTerm2 + zsh&lt;/li&gt;
&lt;li&gt;Vim&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://apps.apple.com/us/app/amphetamine/id937984704?mt=12" rel="nofollow" target="_blank" title=""&gt;Amphetamine&lt;/a&gt;，Mac 防止休眠。&lt;/li&gt;
&lt;li&gt;OmniGraffle，画图工具&lt;/li&gt;
&lt;li&gt;Moom，调节窗口位置和大小。&lt;/li&gt;
&lt;li&gt;老版本 Tower, Git 客户端，当初一次性买断，一直将就着用，从来没升级。 &lt;/li&gt;
&lt;li&gt;Office365，outlook 收邮件，onenote 做笔记&lt;/li&gt;
&lt;li&gt;App cleaner&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://sparkmailapp.com/" rel="nofollow" target="_blank" title=""&gt;Spark&lt;/a&gt;，邮件客户端。&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://code.visualstudio.com/" rel="nofollow" target="_blank" title=""&gt;Visual Studio Code&lt;/a&gt;，敲代码编辑器。&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/features/copilot" rel="nofollow" target="_blank" title=""&gt;copilot&lt;/a&gt;，AI 敲代码工具。 &lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="其他"&gt;其他&lt;/h2&gt;
&lt;p&gt;最近还买了 Yubikey 在玩，但是除了实现无密码登录和两步验证，还没学会别的玩法。&lt;/p&gt;</description>
      <author>xiaoronglv</author>
      <pubDate>Sat, 30 Jul 2022 10:10:46 +0800</pubDate>
      <link>https://ruby-china.org/topics/42563</link>
      <guid>https://ruby-china.org/topics/42563</guid>
    </item>
    <item>
      <title>Atlantis: 可确定的执行计划</title>
      <description>&lt;p&gt;&lt;img src="https://l.ruby-china.com/photo/xiaoronglv/a49b9872-cdf3-42b4-bc96-8ae1092d9f07.png!large" title="" alt=""&gt;&lt;/p&gt;

&lt;p&gt;Workstream 的基础架构完全用 Terraform 来编写，就是大家常说的架构即代码 Infrastructure as Code (IaC)，并且用自动化工具 &lt;a href="https://www.runatlantis.io/" rel="nofollow" target="_blank" title=""&gt;Atlantis&lt;/a&gt; 来执行。每次有工程师要修改架构，只需要修改 Terraform 代码，然后在 Github 提交一个 Pull Request。如果 PR 通过审核，Atlantis 可以帮助执行 terraform apply，并且合并 Pull Request。&lt;/p&gt;

&lt;p&gt;在使用 Atlantis 的过程中我发现它不稳定。&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Atlantis 经常打印出一堆和代码改动无关的执行计划。比如，我只是给一些资源加了一个标签&lt;code&gt;team=Kraken&lt;/code&gt;, Atlantis 在做计划时除了增加 tag，它还要给 Security Group 增加一个 Egress 的规则。这就令代码审核人员困惑。&lt;/li&gt;
&lt;li&gt;一个月前的 Terraform 代码，没有人碰过，再次运行会失败。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Atlantis 执行老代码，变成了一种玄学。代码越老，出问题的概率越大。&lt;/p&gt;
&lt;h2 id="为什么会出现这种情况呢？"&gt;为什么会出现这种情况呢？&lt;/h2&gt;
&lt;p&gt;有些人可能说"你没有使用 &lt;code&gt;.terraform.lock.hcl&lt;/code&gt; 锁定依赖，所以不同的人执行时，会使用不同版本的 module，产生不同的结果。"&lt;/p&gt;

&lt;p&gt;其实加了锁文件也没用。Terraform 的锁文件功能非常薄弱，只能锁定 provider 的版本，并不能锁定 module 版本。当 Atlantis 执行 &lt;code&gt;terraform apply&lt;/code&gt; 时，Terraform 会下载和使用 module 的最新版本，这就导致了预期外的架构变动。&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;a href="https://www.terraform.io/language/files/dependency-lock#dependency-lock-file" rel="nofollow" target="_blank" title=""&gt;Terraform Documentation&lt;/a&gt;:&lt;/p&gt;

&lt;p&gt;At present, the dependency lock file tracks only provider dependencies. Terraform does not remember version selections for remote modules, and so Terraform will always select the newest available module version that meets the specified version constraints. You can use an exact version constraint to ensure that Terraform will always select the same module version.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id="如何避免？"&gt;如何避免？&lt;/h2&gt;
&lt;p&gt;如果我们能锁定 provider 和 module 的版本，Atlantis 执行 terraform plan 或 terraform apply 时，就会自始至终使用你所选取的版本，结果永远是恒定的（Deterministic）。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;首先，在代码中显式的指定 module 的版本，锁死 module 的版本。&lt;/strong&gt;&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;module "webserver_sg" {
  source  = "terraform-aws-modules/security-group/aws"
  version = "4.9.0"  # 👈 看这里

  name        = "${var.project}-webserver"
  vpc_id      = module.vpc.vpc_id

  ingress_with_source_security_group_id = [
    {
      rule = "http-80-tcp"
      source_security_group_id = module.alb_sg.security_group_id
    }
  ]
  tags = local.tags
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;其次，锁定 provider 的版本。&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;最简单的办法是把 .terraform.lock.hcl 添加到 git 中，锁死 provider 的版本。&lt;/p&gt;

&lt;p&gt;这样 Atlantis 在执行代码时，就会稳定一些，避免了各种玄学问题。&lt;/p&gt;

&lt;p&gt;原文链接：&lt;a href="https://mednoter.com/deterministic-atlantis.html" rel="nofollow" target="_blank"&gt;https://mednoter.com/deterministic-atlantis.html&lt;/a&gt;&lt;/p&gt;

&lt;hr&gt;

&lt;p&gt;Workstream is hiring fullstack engineering!&lt;/p&gt;

&lt;p&gt;email: &lt;code&gt;Base64.decode64("cnlhbkB3b3Jrc3RyZWFtLmlz\n")&lt;/code&gt;&lt;/p&gt;</description>
      <author>xiaoronglv</author>
      <pubDate>Tue, 29 Mar 2022 08:16:46 +0800</pubDate>
      <link>https://ruby-china.org/topics/42264</link>
      <guid>https://ruby-china.org/topics/42264</guid>
    </item>
    <item>
      <title>Terraform 入门知识: terraform.lock.hcl 简介</title>
      <description>&lt;p&gt;每种编程语言都有自己的包管理工具，Javascript 有 npm，Ruby 有 Bundler，Python 有 pip。这类管理工具通常会为项目创建一个类似 &lt;code&gt;package.lock&lt;/code&gt; 的文件，锁定项目依赖版本，从而保证在不同的机器上运行程序时，依赖环境都相同。&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;对于 Ruby 项目，这个文件是 Gemfile.lock&lt;/li&gt;
&lt;li&gt;对于前端项目，这个文件是 package-lock.json 或 yarn.lock&lt;/li&gt;
&lt;li&gt;对于 Python 项目，这个文件是 Pipfile.lock&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Terraform 也有“包”的概念，称之为 &lt;a href="https://www.terraform.io/language/modules/develop" rel="nofollow" target="_blank" title=""&gt;module&lt;/a&gt;。每个 module 打包了一堆资源。在代码中引入一个 module，就会创建了这个包中的一堆资源。&lt;/p&gt;

&lt;p&gt;module 有点像肯德基的全家桶。&lt;/p&gt;

&lt;p&gt;你可以啰哩啰嗦的对服务员说“我要 5 块吮指原味鸡、6 块香辣鸡翅、1 盒土豆泥、1 根香甜玉米棒和 3 杯中杯百事可乐”，也可以直接说“我要一份全家桶”。&lt;/p&gt;
&lt;h2 id="module 初体验"&gt;module 初体验&lt;/h2&gt;
&lt;p&gt;比如要在 AWS 中创建一个 web 服务器的安全组（Security Group），需要创建 Security Group，Ingress Rule，Egress Rule。&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;resource "aws_security_group" "web-server" {
  name        = "web-server"
  vpc_id      = module.vpc.vpc_id

  ingress {
    description      = "HTTP"
    from_port        = 80
    to_port          = 80
    protocol         = "tcp"
    cidr_blocks      = "0.0.0.0"
  }

    ingress {
    description      = "HTTPs"
    from_port        = 443
    to_port          = 443
    protocol         = "tcp"
    cidr_blocks      = "0.0.0.0"
  }


  egress {
    from_port        = 0
    to_port          = 0
    protocol         = "-1"
    cidr_blocks      = ["0.0.0.0/0"]
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;但是如果你使用 &lt;a href="https://registry.terraform.io/modules/terraform-aws-modules/security-group/aws/latest" rel="nofollow" target="_blank" title=""&gt;AWS Security Group module&lt;/a&gt; ，几行代码就搞定了。&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;module "web_server_sg" {
  source = "terraform-aws-modules/security-group/aws"
  name        = "web-server"
  description = "Security group for web-server with HTTP ports open within VPC"
  vpc_id      = "vpc-12345678"

  ingress_rules       = ["http-80-tcp", "https-443-tcp"]
  egress_rules        = ["all-all"]
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;你甚至可以在 module 中打包一份 RDS，Redis，Memcached，EC2，S3 的资源全家桶，一口气创建整个 web 应用的技术栈。&lt;/p&gt;
&lt;h2 id=".terraform.lock.hcl 用途"&gt;.terraform.lock.hcl 用途&lt;/h2&gt;
&lt;p&gt;写好了 Terraform 代码，需要先执行 &lt;code&gt;terraform init&lt;/code&gt; 初始化，这条命令会创建一个文件 .terraform.lock.hcl。&lt;/p&gt;

&lt;p&gt;熟悉其他编程语言的程序员，往往会误解这个文件，认为它是 module 的版本锁定文件。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;事实上它并不锁定  module 的版本，它只锁定 provider 的版本。&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Terraform Documentation:&lt;/p&gt;

&lt;p&gt;At present, the dependency lock file tracks only provider dependencies. Terraform &lt;code&gt;does not&lt;/code&gt; remember version selections for remote modules, and so Terraform will always select the newest available module version that meets the specified version constraints. You can use an exact version constraint to ensure that Terraform will always select the same module version.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=".terraform.lock.hcl 中有什么？"&gt;.terraform.lock.hcl 中有什么？&lt;/h2&gt;
&lt;p&gt;这是一个 &lt;code&gt;.terraform.lock.hcl&lt;/code&gt; 范例，里面并没有各种 module 的版本信息，那它有什么？&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;1. provider "registry.terraform.io/hashicorp/aws" {
2.   version     = "3.63.0"
3.   constraints = "&amp;gt;= 2.7.0, &amp;gt;= 2.42.0, &amp;gt;= 2.49.0, &amp;gt;= 3.27.0, 3.63.0"
4.   hashes = [
5.     "h1:Z+2GvXLgqQ/uPMH8dv+dXJ/t+jd6sriYjhCJS6kSO6g=",
6.     "h1:lf8Qex8bhCmh8TUEAU6H4brzjy3+d4BXB6gcOYnNtNY=",
7.     "zh:42c6c98b294953a4e1434a331251e539f5372bf6779bd61ab5df84cac0545287",
8.     "zh:5493773762a470889c9a23db97582d3a82035847c8d3bd13323b4c3012abf325",
9.     "zh:550d22ff9fed4d817a922e7b84bd9d1f2ef8d3afa00832cf66b8cd5f0e6dc748",
10.    "zh:632cb5e2d9d5041875f57174236eafe5b05dbf26750c1041ab57eb08c5369fe2",
11.    "zh:7cfeaf5bde1b28bd010415af1f3dc494680a8374f1a26ec19db494d99938cc4e",
12.    "zh:99d871606b67c8aefce49007315de15736b949c09a9f8f29ad8af1e9ce383ed3",
13.    "zh:c4fc8539ffe90df5c7ae587fde495fac6bc0186fec2f2713a8988a619cef265f",
14.    "zh:d0a26493206575c99ca221d78fe64f96a8fbcebe933af92eea6b39168c1f1c1d",
15.    "zh:e156fdc964fdd4a7586ec15629e20d2b06295b46b4962428006e088145db07d6",
16.    "zh:eb04fc80f652b5c92f76822f0fec1697581543806244068506aed69e1bb9b2af",
17.    "zh:f5638a533cf9444f7d02b5527446cdbc3b2eab8bcc4ec4b0ca32035fe6f479d3",
18.   ]
19. }
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;让我们来逐行分析这个文件。&lt;/p&gt;

&lt;p&gt;第 1 行，你的 terraform 代码依赖  aws provider。&lt;/p&gt;

&lt;p&gt;第 2 行，provider 的 version。由 terraform 根据一些规则帮你选定。&lt;/p&gt;

&lt;p&gt;第 3 行，constraints 表示 provider 版本的限制条件。你的代码中引入了多个 module 后，每个 module 都对 aws provider 有版本要求，于是 Terraform 就把它们汇总了起来。所以第 2 行的版本其实是由第 3 行的 constraints 决定的。&lt;/p&gt;

&lt;p&gt;第 4 行，hashes 代表 checksum，用来确保下载的 provider 并没有被人恶意篡改过。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;什么是 checksum？&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;文件经过加密函数处理后，都会生成唯一对应的哈希值，不同文件有不同的哈希值。&lt;/p&gt;

&lt;p&gt;文件 1 ---- 加密函数 ----&amp;gt; 哈希值 1&lt;code&gt;a7iwc&lt;/code&gt;  &lt;/p&gt;

&lt;p&gt;文件 2 ---- 加密函数 ----&amp;gt; 哈希值 2 &lt;code&gt;ufj4c&lt;/code&gt; &lt;/p&gt;

&lt;p&gt;文件 3 ---- 加密函数 ----&amp;gt; 哈希值 3 &lt;code&gt;4iEDH&lt;/code&gt; &lt;/p&gt;

&lt;p&gt;文件 4 ---- 加密函数 ----&amp;gt; 哈希值 4 &lt;code&gt;xca3t&lt;/code&gt; &lt;/p&gt;

&lt;p&gt;checksum 用于“文件防篡改”。比如 Java 官方网站会挂出每个版本的下载地址，但是它的下载速度太慢了。于是你在一些非官方的镜像网站下载了 Java，但是又可能怎么保证这些文件是安全的，没有篡改过呢？&lt;/p&gt;

&lt;p&gt;你可以用加密函数对下载的文件进行计算，得到一个哈希值。&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;如果你得到的哈希值与 Java 官网一致，说明文件没被篡改。&lt;/li&gt;
&lt;li&gt;如果你得到的哈希值和官网不一致，说明文件被人动过手脚了&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;img src="https://mednoter.com/media/files/2022/2022-03-21_18-44-02.jpg" title="" alt=""&gt;&lt;/p&gt;

&lt;p&gt;Terraform 中的 hashes 也是类似道理，当 Terraform 发布某个版本的 provider 时，用特定的哈希算法为该版本创建哈希值。如果你下载的文件和这个 hash 值对不上，就表示文件被篡改了。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;为什么有的哈希值的前缀是 &lt;code&gt;h1:&lt;/code&gt;，有些前缀是 &lt;code&gt;zh&lt;/code&gt;？&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;h1 和 zh 代表了不同的哈希算法，所以会产生两份哈希值。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;既然一个特定版本会产生两份哈希值，为什么示例文件有 12 个哈希值？&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;这可能与我运行了以下命令又关系。&lt;/p&gt;

&lt;p&gt;我的同事有些使用 Mac 上，有些使用 Windows 上，有些使用 Ubuntu 上，此外 Atlantis 运行在 Ubuntu 上。所以我执行了以下命令，把所有平台的哈希值都计算好了，这样同事们在自己的电脑或服务器上执行。&lt;/p&gt;

&lt;p&gt;我猜测：&lt;/p&gt;

&lt;p&gt;哈希值的数量 = 版本 * 平台数量 * 哈希函数数量&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;terraform providers lock \
  -platform=linux_arm64 \
  -platform=linux_amd64 \
  -platform=darwin_amd64 \
  -platform=windows_amd64
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;既然  &lt;code&gt;.terraform.lock.hcl&lt;/code&gt;  文件无法锁定 module 的版本，如何锁定 module 的版本号呢？&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;我们只能在定义 module 时显式的指定版本号，作为一个折衷方案。&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;module "vpc" {
  source  = "terraform-aws-modules/vpc/aws"
  version = "2.21.0"  #指定版本号

  name            = "${var.project}-${var.environment}"
  cidr            = var.vpc_cidr
  azs             = var.vpc_azs
  public_subnets  = var.vpc_public_subnets
  private_subnets = var.vpc_private_subnets
  tags            = local.tags
}

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;所以这个文件真是既容易误解，又有点鸡肋。&lt;/p&gt;

&lt;p&gt;看完本文，如果非要记住一个结论，那就是 &lt;strong&gt;.terraform.lock.hcl 并不锁定  module 的版本，它只锁定 provider 的版本。&lt;/strong&gt;。&lt;/p&gt;
&lt;h2 id="Reference"&gt;Reference&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href="https://stackoverflow.com/questions/71554766/terraform-does-terraform-lock-hcl-lock-the-version-of-each-terraform-module" rel="nofollow" target="_blank" title=""&gt;Stackoverflow: does ".terraform.lock.hcl" lock the version of each terraform module?&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.terraform.io/language/files/dependency-lock#lock-file-location" rel="nofollow" target="_blank" title=""&gt;Terraform documentation: Dependency Lock File&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;hr&gt;

&lt;p&gt;原文链接：&lt;a href="https://mednoter.com/terraform-lock-file.html" rel="nofollow" target="_blank"&gt;https://mednoter.com/terraform-lock-file.html&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;欢迎订阅我的博客。&lt;/p&gt;

&lt;hr&gt;

&lt;p&gt;Workstream is hiring fullstack engineering!&lt;/p&gt;

&lt;p&gt;email: &lt;code&gt;Base64.decode64("cnlhbkB3b3Jrc3RyYWVtLmlz\n")&lt;/code&gt;&lt;/p&gt;</description>
      <author>xiaoronglv</author>
      <pubDate>Mon, 21 Mar 2022 21:04:07 +0800</pubDate>
      <link>https://ruby-china.org/topics/42244</link>
      <guid>https://ruby-china.org/topics/42244</guid>
    </item>
    <item>
      <title>[2022 年 3 月 22 日] Ruby Tuesday 线上聚会：如何找国外的远程工作？</title>
      <description>&lt;p&gt;主题：如何找国外的远程工作？&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;脱离内卷，找条活路&lt;/li&gt;
&lt;li&gt;继续写 Ruby or whatever language you love&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;主持人：吕小荣 / Teddy&lt;/p&gt;

&lt;p&gt;嘉宾：机械唯物主义 / 吕戈 / 电鸭社区的大灰&lt;/p&gt;

&lt;p&gt;时间：2022 年 3 月 22 日 (周二) 19:15 - 21:15&lt;/p&gt;

&lt;p&gt;地点：腾讯会议&lt;/p&gt;
&lt;h2 id="Agenda"&gt;Agenda&lt;/h2&gt;&lt;h3 id="1. 嘉宾自我介绍"&gt;1. 嘉宾自我介绍&lt;/h3&gt;&lt;h3 id="2. 各自分享自己是如何找到远程工作的。"&gt;2. 各自分享自己是如何找到远程工作的。&lt;/h3&gt;
&lt;p&gt;找国外工作的各种渠道&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;大部分都是朋友推荐 &lt;/li&gt;
&lt;li&gt;&lt;a href="https://eleduck.com/" rel="nofollow" target="_blank" title=""&gt;电鸭社区&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://indeed.com/" rel="nofollow" target="_blank" title=""&gt;indeed.com&lt;/a&gt;，是美国最大的招工平台。&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://angel.co/" rel="nofollow" target="_blank" title=""&gt;angel.co&lt;/a&gt;，非常早期的创业公司会在上面发布职位。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="3. 如何收美元的工资"&gt;3. 如何收美元的工资&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;中国的银行卡是可以收美元的&lt;/li&gt;
&lt;li&gt;香港的银行卡&lt;/li&gt;
&lt;li&gt;payoneer&lt;/li&gt;
&lt;li&gt;deel&lt;/li&gt;
&lt;li&gt;走外贸公司（灰色地带）&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="4. 如何报税。"&gt;4. 如何报税。&lt;/h3&gt;
&lt;p&gt;目前税务是灰色地带，没有人查我们这些小虾米，但是请依法纳税。你可以和雇主多要点钱，但是尽量不要逃税。&lt;/p&gt;

&lt;p&gt;&lt;a href="https://eleduck.com/posts/oQfg7v" rel="nofollow" target="_blank"&gt;https://eleduck.com/posts/oQfg7v&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://eleduck.com/posts/OGfwZV" rel="nofollow" target="_blank"&gt;https://eleduck.com/posts/OGfwZV&lt;/a&gt;&lt;/p&gt;
&lt;h3 id="4. 如何管好自己"&gt;4. 如何管好自己&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;book: 《掌控习惯》：任何打断都包括，提示，渴望，行动。所以先把提示去掉，比如把手机放的远远的。&lt;/li&gt;
&lt;li&gt;&lt;a href="https://ruby-china.org/topics/39469" title=""&gt;远程办公如何提高自制力&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="5. 国外公司常用工具"&gt;5. 国外公司常用工具&lt;/h3&gt;
&lt;p&gt;Slack / Google Workspace / Jira / Trello 等等&lt;/p&gt;
&lt;h3 id="6. 国外公司的文化"&gt;6. 国外公司的文化&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;不会太 push&lt;/li&gt;
&lt;li&gt;不会周六周末或非工作时间骚扰你&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="7. 喜欢远程工作的原因"&gt;7. 喜欢远程工作的原因&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;兼顾家庭，尤其是可以多花点时间照顾孩子。&lt;/li&gt;
&lt;li&gt;自由安排时间。&lt;/li&gt;
&lt;li&gt;节省通勤的时间，可以去健身，学习&lt;/li&gt;
&lt;li&gt;如果你生活在二三线城市，通过远程，你可以拿一个比较体面的薪水。但是如果你在大城市，远程工作赚的钱可能没有字节跳动，阿里等大厂那么多。&lt;/li&gt;
&lt;li&gt;很多远程工作者，都有个创业梦，可以在业余时间多准备和学习。&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;总之，不要把省出来的时间摸鱼，蹉跎岁月，这是对生命的浪费。&lt;/p&gt;
&lt;h3 id="8. 雇主为什么招聘中国远程程序员？"&gt;8. 雇主为什么招聘中国远程程序员？&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;欧美程序员也都想进大厂拿高薪，所以的中小企业不太好招人。中国劳动力市场丰富，给他们机会。&lt;/li&gt;
&lt;li&gt;雇主的老板可能有华人的元素，比如创始人是华裔，和国内的程序员沟通没有语言障碍。&lt;/li&gt;
&lt;li&gt;中国的程序员综合素质高。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="9. 雇主在招聘中国远程程序员时的顾虑"&gt;9. 雇主在招聘中国远程程序员时的顾虑&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;中美政治紧张，国家层面都不希望自己的工作岗位外流&lt;/li&gt;
&lt;li&gt;数据安全。国外前端岗位明显多于招后端岗位。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="10. 英文要求。"&gt;10. 英文要求。&lt;/h3&gt;
&lt;p&gt;英语读写是基本要求。听说是加分项，不是强制的，欧美的企业如果打算在中国找人，心里大概已经知道大家口语不太好了。&lt;/p&gt;

&lt;p&gt;关于英语的学习&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;英语的学习是个长期的过程，不要临时抱佛脚。&lt;/li&gt;
&lt;li&gt;用英语提前准备好常见面试题的答案&lt;/li&gt;
&lt;li&gt;简历留几个槽点，引导面试官问自己准备好的槽点&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;一些比较好的练习口语的资源&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;a href="https://italki.com" rel="nofollow" target="_blank"&gt;https://italki.com&lt;/a&gt;  可以约外教聊天。&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id="11. 国外远程公司的优缺点"&gt;11. 国外远程公司的优缺点&lt;/h3&gt;
&lt;p&gt;缺点&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;克服时差&lt;/li&gt;
&lt;li&gt;职业天花板，很难有华人高管。&lt;/li&gt;
&lt;li&gt;因为远程，所以同事之间的关系比较淡&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;优点&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;work life balance&lt;/li&gt;
&lt;li&gt;人性化&lt;/li&gt;
&lt;li&gt;老板素质比较高&lt;/li&gt;
&lt;li&gt;看结果，不太在意你的学历。&lt;/li&gt;
&lt;li&gt;看结果，不太在意你的年龄。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="12. 怎么交五险一金？"&gt;12. 怎么交五险一金？&lt;/h3&gt;
&lt;p&gt;很多国外的小公司在国内没有注册公司，所以没办法给你交五险一金。这并不代表他们是骗子。你可以在淘宝/蚂蚁社保上代缴。&lt;/p&gt;

&lt;p&gt;如果你要在北京上海落户，五险一金很重要，请慎重选择远程办公。还是待在正规的公司，拿到户口更重要。&lt;/p&gt;
&lt;h2 id="小福利"&gt;小福利&lt;/h2&gt;
&lt;p&gt;参加本次聚会可以拿到 RubyMine license，现场抽奖。&lt;/p&gt;
&lt;h2 id="Workstream 还在招聘"&gt;Workstream 还在招聘&lt;/h2&gt;
&lt;p&gt;最后，作为主持人，夹点私货，&lt;strong&gt;&lt;a href="https://www.workstream.us/" rel="nofollow" target="_blank" title=""&gt;Workstream&lt;/a&gt; 还在招聘 Ruby 程序员&lt;/strong&gt; ，感兴趣的可以单独联系我。&lt;/p&gt;
&lt;h2 id="使用 Ruby 的公司"&gt;使用 Ruby 的公司&lt;/h2&gt;
&lt;p&gt;Goat / Flexport / Chowbus / SAP Jam Team / SAP SuccessFactor Mobile Team / Gitlab / Strikingly，这些公司都在使用 Ruby。&lt;/p&gt;
&lt;h2 id="聚会的录像"&gt;聚会的录像&lt;/h2&gt;
&lt;p&gt;bilibili: 
&lt;span class="embed-responsive embed-responsive-16by9"&gt;&lt;iframe class="embed-responsive-item" src="//player.bilibili.com/player.html?bvid=1R34y1x7QW" allowfullscreen=""&gt;&lt;/iframe&gt;&lt;/span&gt;/&lt;/p&gt;

&lt;p&gt;Youtube: 
&lt;span class="embed-responsive embed-responsive-16by9"&gt;&lt;iframe class="embed-responsive-item" src="//www.youtube.com/embed/QvJSHzfKOkQ" allowfullscreen=""&gt;&lt;/iframe&gt;&lt;/span&gt;&lt;/p&gt;

&lt;p&gt;录音下载：&lt;/p&gt;

&lt;p&gt;&lt;a href="https://drive.google.com/file/d/1Zc6y91QsN0GpAUHX-JJ9gJ1OOOr4ROJE/view" rel="nofollow" target="_blank"&gt;https://drive.google.com/file/d/1Zc6y91QsN0GpAUHX-JJ9gJ1OOOr4ROJE/view&lt;/a&gt;&lt;/p&gt;</description>
      <author>xiaoronglv</author>
      <pubDate>Fri, 11 Mar 2022 11:05:36 +0800</pubDate>
      <link>https://ruby-china.org/topics/42201</link>
      <guid>https://ruby-china.org/topics/42201</guid>
    </item>
    <item>
      <title>请问怎么让 CircleCI 跑子目录的测试代码？</title>
      <description>&lt;p&gt;&lt;a href="https://github.com/CircleCI-Public/circleci-demo-ruby-rails/blob/master/.circleci/config.yml" rel="nofollow" target="_blank" title=""&gt;CircleCI&lt;/a&gt; 是一个分布式的跑测试的云产品，本来一个小时的测试，它拆成 80 份，几分钟就可以跑完。此外它和 Ruby on Rails 的集成非常方便，官方还提供了实例代码。只要在代码的根目录，添加一个 &lt;code&gt;.circleci/config.yml&lt;/code&gt; 文件就可以了。每次有人创建 PR，会自动触发 CI。&lt;/p&gt;

&lt;p&gt;但是我最近遇到了一个问题，我的新项目是前后端分离的，前端一个文件夹，后端一个文件夹。&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;后端是 RoR，用来做 API&lt;/li&gt;
&lt;li&gt;前端 react&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;有没有人设置过 CircleCI 去跑两个文件夹中的测试吗？我折腾了半天，还是没搞定。&lt;/p&gt;

&lt;p&gt;&lt;img src="https://l.ruby-china.com/photo/xiaoronglv/9f85bf8f-36da-45c8-85ed-8ea73ddfff4d.png!large" title="" alt=""&gt;&lt;/p&gt;</description>
      <author>xiaoronglv</author>
      <pubDate>Thu, 10 Mar 2022 15:30:12 +0800</pubDate>
      <link>https://ruby-china.org/topics/42198</link>
      <guid>https://ruby-china.org/topics/42198</guid>
    </item>
    <item>
      <title>Foreman 是干什么的？ 为什么不用 systemd ？</title>
      <description>&lt;p&gt;我在看一些项目的部署文档时，有些人提到了 Foreman，看样子它是用来守护进程的。比如 puma/sidekiq/racecar 之类的进程挂了，foreman 可以自动把它们启动。&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/zendesk/racecar#deploying-consumers" rel="nofollow" target="_blank"&gt;https://github.com/zendesk/racecar#deploying-consumers&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;If you're already deploying your Rails application using e.g. Capistrano, all you need to do to run your Racecar consumers in production is to have some process supervisor start the processes and manage them for you.&lt;/p&gt;

&lt;p&gt;Foreman is a very straightford tool for interfacing with several process supervisor systems. You define your process types in a Procfile, e.g.&lt;/p&gt;

&lt;p&gt;racecar-process-payments: bundle exec racecar ProcessPaymentsConsumer
racecar-resize-images: bundle exec racecar ResizeImagesConsumer&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id="问题"&gt;问题&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;foreman 可以守护其他进程，如果 foreman 自己挂了，谁把它启动啊？&lt;/li&gt;
&lt;li&gt;为什么大家不直接用 systemd？&lt;/li&gt;
&lt;li&gt;foreman 出生时，systemd 还没诞生（或成熟）吗？&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;熟悉 Linux 的朋友，可以讲一讲历史岁月吗？&lt;/p&gt;</description>
      <author>xiaoronglv</author>
      <pubDate>Thu, 14 Oct 2021 10:11:14 +0800</pubDate>
      <link>https://ruby-china.org/topics/41770</link>
      <guid>https://ruby-china.org/topics/41770</guid>
    </item>
    <item>
      <title>Gemfile.lock 中的 Platform 是什么意思？</title>
      <description>&lt;p&gt;我们团队内的程序员用的电脑系统各不相同，有人用 windows，有人用 Mac，有人用 Ubuntu，我注意到 Gemfile.lock 中的 Platform 经常会变来变去。&lt;/p&gt;

&lt;p&gt;&lt;img src="https://l.ruby-china.com/photo/xiaoronglv/a3a1081e-4bcc-498f-9fff-cc57df9e7f1f.png!large" title="" alt=""&gt;&lt;/p&gt;

&lt;p&gt;尤其是创建一个 microservice 时，跑测试时必定会遇到一个问题。&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Your bundle only supports platforms ["x86_64-darwin-19"] but your local
platform is x86_64-linux. Add the current platform to the lockfile with 
`bundle lock --add-platform x86_64-linux` and try again.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;引起这个问题的原因：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;新项目是在 Mac 上创建的。&lt;/li&gt;
&lt;li&gt;测试是在 linux 上跑的。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="问题"&gt;问题&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Gemfile.lock 中这段代码是什么意思呢？&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;为什么 Bundler 要创建这个声明？&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;这个声明是为了解决什么问题？&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;是与可执行文件有关系吗？&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;PLATFORMS
  ruby
  x86_64-darwin-19
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id="猜测"&gt;猜测&lt;/h2&gt;
&lt;p&gt;猜测 1: 某些 gem 自带二进制可执行文件，只能在某个平台运行。&lt;/p&gt;

&lt;p&gt;猜测 2: Platform 是指定&lt;code&gt;支持的 Ruby 解析器&lt;/code&gt;。&lt;/p&gt;
&lt;h2 id="参考资料"&gt;参考资料&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://www.moncefbelyamani.com/understanding-the-gemfile-lock-file/#platforms" rel="nofollow" target="_blank"&gt;https://www.moncefbelyamani.com/understanding-the-gemfile-lock-file/#platforms&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://bundler.io/man/gemfile.5.html#PLATFORMS" rel="nofollow" target="_blank"&gt;https://bundler.io/man/gemfile.5.html#PLATFORMS&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/rubygems/rubygems/issues/4269#issuecomment-758564690" rel="nofollow" target="_blank"&gt;https://github.com/rubygems/rubygems/issues/4269#issuecomment-758564690&lt;/a&gt;&lt;/p&gt;</description>
      <author>xiaoronglv</author>
      <pubDate>Mon, 27 Sep 2021 11:12:09 +0800</pubDate>
      <link>https://ruby-china.org/topics/41719</link>
      <guid>https://ruby-china.org/topics/41719</guid>
    </item>
  </channel>
</rss>
