移动应用开发手册16:CI/CD控制论——如何用系统工程对抗熵增巨婴

前言

20xx年x月x日。

有这么一个公司项目组。

负责人制订了流程,设计了模板。

而他的员工们,全都不作为,想着老板会兜底。

最终,项目彻底崩溃。负责人扛下了所有锅。

他在绝望地嘶吼着,喊着"我有事",然而自始至终都没有人听见。

别觉得上述例子沉痛过了头,那是因为你不是那个抗伤的。这种例子,随便哪个场合都有可能发生,不论是竞赛战队、企业工作,还是科学研究、课程项目,都会有。


第一部分:什么是CI/CD(给没看过往期的补个课)

一句话版

CI/CD,全称为持续集成与持续交付系统。

门禁系统 :不刷卡不让进,不达标不让合。

自动流水线:你只管提交,测试、编译、部署它自己跑。

技术人话版

CI(持续集成/Continuous Integration) :你每次提交代码,系统自动编译、自动测试、自动校验,不通过就直接打回去,不用收拾烂摊子了。

CD(持续交付/Continuous Delivery) :测试通过后,系统自动部署到目标环境,不用你手动部署、手动重启、手动改配置。

合起来,CI/CD就是一套代码从提交到上线全自动、可拦截、可追溯的流水线系统。

没有CI/CD的系统

  • 代码质量靠人盯:谁认真谁不认真,全凭自觉。
  • 提交记录靠手查:出了问题翻聊天记录,翻不出来就扯皮。
  • 上线部署靠手动:传包、重启、改配置,一步错步步错。
  • 规范执行靠喊话:你说"别force push",他偏force push,你除了骂人没别的办法。

有CI/CD的系统

  • 代码质量靠系统拦:测试不通过,CI直接拒绝合并,自动近防炮,你值得信赖。
  • 提交记录靠系统存:谁在何时何地改了什么,git log里全有,之前第12期说过,详情可从合集跳转。
  • 上线部署靠系统跑:你只管提交,剩下的它自己来。自动化流水线,编译部署一条龙。
  • 规范执行靠系统强制:你force push?CI直接拦截。你想绕过?由不得你。

没有CI/CD的团队,像没有交通信号灯的路口:谁技术好谁先过,谁不要命谁先过,谁脸皮厚谁先过。看着好像也能通车,但早晚撞上。横冲直撞的,那叫自甘风险,规矩办事但出事的,那叫倒霉蛋。

有CI/CD的团队,像装了红绿灯和电子警察的路口:红灯一亮,谁都得停。你闯红灯,探头一拍,𡂿,6分没了。不需要交警站在路口骂人,系统替你管。(注:探头,上海话特指道路违章监控摄像)


第二部分:CI/CD的定位作用

CI/CD不能替代人类的进攻性价值,但能完美适配人类的防御性需求。

人类的进攻性价值 :设计架构、定义接口、判断优先级、做技术选型、写核心逻辑。

人类的防御性需求:不让乱提交、不让跳过测试、不让force push、不让封仓后动手。

CI/CD管的是后者。它不替你" ",它替你""。

作用1:拦截内奸

CI的第一作用不是"帮你做事",是"不让内奸乱来"。

  • 测试没过?不准合并。
  • 代码规范不过?不准合并。
  • 时间过了封仓节点?不准提交。
  • 你没权限?动都别想动。403伺候。

CI不是你的助手,CI是门卫。 你符合标准,它放你过。你不符合标准,它拦你。它不跟你讲情面,不跟你商量,不跟你"这次算了"。🎶开封有个包青天~铁面无私辨忠奸~

拦截的本质是:把"人盯人"变成"系统盯人"。

作用2:巡天雷达

CI的第二大作用,便是信息归档。

  • 谁提了PR?
  • PR有没有经过实践检验?
  • 如果没有,这个PR在哪一步挂掉的?

这些可全在git log里躺着。甚至有些强大的CI能够邮件通知肇事者。

你不要以为你出了事情没人知道。老大哥可在盯着你呢。

巡天雷达扫没扫不重要,重要的是你家内奸相信它会扫。

CI的防守反击能力,比你想的要强得多。

作用3:绝对壁垒

CI的第三大作用,便是保证内奸的一切不合规操作失效。

包括但不限于:"我没看到"、"通融一下"、"就这一次"、"咕咕咕"等一系列熟人社会卖萌撒泼操作。人会心软,CI不会。

  • 你想force push?CI说不行。
  • 你想跳过测试?CI说不行。
  • 你想在封仓后提交?CI说不行。
  • 你想"就这一次"?CI说不行。

无数个案例表明这些所谓的"通融"会导致放纵和事故。故而,工程环境,要的是一个不听任何人多解释的仲裁系统。

作用4:圣洁守护

CI对于内奸,是限制。对于忠臣,则是最有力的保障之一。

  • 你的代码,不会被别人玷污。
  • 你的数据,不会被别人篡改。
  • 你的东西,不会被别人砸烂。

忠臣们无疑是不想被内奸折磨的。谁都不想自己的功劳被一个没有规矩的人砸烂。

作用5:系统法制

费孝通先生的《乡土中国》说过,城市,是一个法制体系。

没有CI :规矩靠话事人喊,话事人一走,规矩就散。

有CI:规矩写在配置里,谁走都不影响。

CI把"某个人在盯着"变成了"系统在盯着"。 你不需要一个"暴君"来管团队,你需要一套"法律"来管团队。CI就是那套法律。它能够强制落实,不讲人情。

治理的本质是:把"人在,规矩在 "变成"系统在,规矩在 "。

你不需要一个人格化的"暴君"。你需要一套非人格化的"制度"。

CI就是那个制度。它不生气,不偏心,不"这次算了"。它只按写好的规则执行。


第三部分:四国大战(方式对比)

没错。这次实验规模升级了,以往是只有萌新vs老鸟的单变量思维对比,今天,升级到双变量。定义如下:

萌新思维 :巨婴逻辑,做事不考虑其他人,只想着自己,且出了问题就想赖。

老鸟思维:职场逻辑,料事长远,如无必要,绝不破坏流程。

仍然是以企业github项目为例。

A组:全队萌新/无CI

首先最重要的是什么?萌新不一定会考虑瞎搞事的后果。

前面第12篇已经骂过force push的后果,一推上去,人家的成果白费。人家想骂你,但找不到任何一个合法骂你的窗口。

最终,内部扯皮是必然的,团队崩溃也是必然的。

核心:没有强制规则,就没有问罪的流程。

B组:全队老鸟/无CI

一般而言,老鸟不像A组萌新,能够考虑到别人的成果。但这个组合的问题在哪?

还得靠人盯人完成控制。

但凡话事人请假几天,整个队伍就会六神无主,最终可能在虚无中崩掉。

核心:人在规矩在,人走了,规矩就没了。

C组:全队萌新/有CI

和A组本质的区别就是:用系统封锁这些萌新可能的作妖路径。

萌新巨婴思维在职场,说难听点,那就是公敌。他们不接受控制?他们要自由?那就别在企业干工程岗。这不是给他们自由的温柔乡。

钱学森钱老在《工程控制论》说过:控制论的基本问题,其关键,就在于存在各种不确定因素。

我们则可以通过限制变量参数,将结果约束到可控制范围之内。

核心:愿意学的萌新,很好,你已经迈出了成为老鸟的第一步;不愿意学的......这个环境容不得你。

D组:全队老鸟/有CI

如果说C组的CI对交付是雪中送炭,D组则是锦上添花。

哪怕话事人走了,交付流程变了,老鸟看一眼CI就知道该怎么做。

老鸟能克隆仓库,自然可以看CI代码。看得见≠改得了。

老鸟本来就知道该守规范,CI一加,疲劳驾驶风险也大幅度降低。

最终,这一组是能够直接自动化运行的。

核心:老鸟保平时,CI保意外,整个团队滴水不漏。

四组对比总表

组别 人员构成 CI状态 靠什么管 话事人走了会怎样 CI的作用 核心结论
A 全队萌新 无CI 必然崩 没有强制规则,就没有问罪的流程
B 全队老鸟 无CI 人盯人 可能崩 人在规矩在,人走了规矩就没了
C 全队萌新 有CI 系统强制 规矩还在 封锁作妖路径 愿意学的迈出第一步,不愿意的容不下你
D 全队老鸟 有CI 系统+人 规矩不变 自动化运行 老鸟保平时,CI保意外,滴水不漏

第四部分:四国大战扯皮日常(吐槽还是有)

这次由于是双变量实验,吐槽不是萌新对老鸟这么简单,而是BC可能骂A,D可能骂B的情况。

场景1:A组萌新被D组CI驳回

A:我本地跑得好好的,怎么CI给我驳回了?肯定是CI有问题。

D(冷笑):你本地跑得好好的?你本地连的是localhost,CI连的是测试环境。你当CI是你家路由器啊?

A:那我把URL改成测试环境的不就行了?

D:你改URL,你测试跑了吗?你字段对齐了吗?你连git commit message都写的是'fix',CI不驳你驳谁?

A:那我不干了行不行?

D:行啊,门在那边。CI不拦你,门也不拦你。你这是不能胜任工作,我们证据摆着,你哪怕仲裁也没有N。我们只要熬过30天就能把你合法辞退。哦等会,忘了你试用期,根据《劳动合同法》39条,你连那30天机会都没有,直接卷铺盖吧,不送,老板等会会把纸质版辞退通知书送到你家。😃

《劳动合同法》相关法条原文

  第三十九条 用人单位单方即时解除劳动合同

  劳动者有下列情形之一的,用人单位可以解除劳动合同:

  (一)在试用期间被证明不符合录用条件的;

  (二)严重违反用人单位的规章制度的;

  (三)严重失职,营私舞弊,给用人单位造成重大损害的;

  (四)劳动者同时与其他用人单位建立劳动关系,对完成本单位的工作任务造成严重影响,或者经用人单位提出,拒不改正的;

  (五)因本法第二十六条第一款第一项规定的情形致使劳动合同无效的;

  (六)被依法追究刑事责任的。

场景2:B组和D组论CI/CD

B:CI/CD?这玩意配置好像很麻烦欸。这玩意我20年都没用过。我没违反过规范,也没出过事故。

D:哦?20年?你的运气确实挺好。

D:不过这东西对你也有好处。如果你作为一个带团队的指挥,出了什么意外情况缺了席,只要CI在,这玩意能够护你周全。

B:怎么说?这玩意一次部署就能够替我自动拦截吗?

D:没错。培训学习成本是一次性的。收益是长期计算的。这玩意对你,稳赚不赔,都是守规矩的人,我不用替你计算。

场景3:A组和B组论秩序

A:为什么非得讲秩序?!我要自由!我要创造!

B:......

B:老板我想申请调组。

场景4:C组的逆袭

A:C,你到底是怎么转正的,我又被开了!!

C:两个字,规矩。

C:四个字,建设规矩。

C:理解规矩,利用规矩,建设规矩。我已经不是那种巨婴了,我破茧重生了。

第五部分:一个相对完善的CI系统分工

(以github action为例)

模块1:时间铡刀模块

拦截类型 :封仓节点之后的任何提交。注意:该模块的判定条件是模块实际运行时间部分老板可能会把时间铡刀模块放到最后,甚至可能双铡刀,专治"卡点主义",工程上这种战术叫做"延迟门禁策略"。你要是不提早交卷还合不进去。就像赶飞机,有机票也没用,必须提早到,拿到你的登机牌。

场景:某科研竞赛封仓前5分钟,萌新想起来"模型还有个参数没改",直接在封仓后提交。封仓文件被覆盖,论文数据对不上。

实现例

yaml 复制代码
on:
  push:
    branches: [main]
jobs:
  time_gate:
    runs-on: ubuntu-latest
    steps:
      - name: Check deadline
        run: |
          DEADLINE="2026-09-20T20:00:00Z"
          NOW=$(date -u +%Y-%m-%dT%H:%M:%SZ)
          if [[ "$NOW" > "$DEADLINE" ]]; then
            echo "::error::封仓时间已过,拒绝合并"
            exit 1
          fi

核心逻辑 :时间不是"建议",是硬边界封仓时间一到,CI直接拦掉 。你想"再改一下",系统不让。特别是"延迟门禁"打法,吃过一次亏你就不想吃第二次。

模块2:越权控制模块

拦截类型 :跨目录force push,尤其是涉及别人模块的操作。

场景:萌新要改自己的前端组件,顺手把后端接口目录也force push了。后端老鸟写了三天的接口,没了。

实现例:用 GitHub Actions 配合 paths 过滤 + CODEOWNERS 文件。每个目录有对应的 owner,你改别人的目录,CI 直接拒绝合并。

复制代码
on:
  pull_request:
    paths:
      - 'backend/**'
jobs:
  check_ownership:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Check if author owns the changed path
        run: |
          AUTHOR=${{ github.actor }}
          CHANGED=$(git diff --name-only origin/main...HEAD)
          # 检查 changed 文件是否在作者负责的目录下
          # 不是 → 拒绝

核心逻辑 :权限不是"看你自觉",是按文件夹锁死。你只能动你的模块,动别人的,CI直接拒绝,不用别人出手。

模块3:自动编译模块

拦截类型 :本地能跑、CI跑不起来的代码。

场景 :萌新本地跑得好好的,CI里连编译都过不了。因为本地有缓存,CI是干净环境。专治"本地跑得好好的"主义。

实现例

以Java程序项目为例。

复制代码
jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Set up JDK
        uses: actions/setup-java@v4
        with:
          java-version: '17'
      - name: Build
        run: ./gradlew build

核心逻辑:本地是"你的环境",CI是"目标环境"。你在本地跑通不算数,CI跑通才算数。

模块4:成品校验模块

拦截类型 :测试没通过、跳过测试、假装测过。报告没写或者根本来不及写。

场景:萌新说"我测过了",CI一跑,测试用例全挂。他说"我本地测过的",但他本地根本没跑测试。

实现例

以Java程序项目为例。

复制代码
jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Run tests
        run: ./gradlew test
      - name: Upload test report
        if: always()
        uses: actions/upload-artifact@v4
        with:
          name: test-report
          path: build/reports/tests/

核心逻辑:测没测过,不是靠嘴说,是靠CI记录。CI里没有测试通过记录,就不给合并。

模块5a-科研型:追根溯源模块

拦截类型 :数据来源不明、模型版本不匹配、结果无法复现。

场景:数模竞赛某团队,建模手改了模型,代码手没改代码。论文里的数据对不上,没人知道哪个是"最终版"。

实现例

复制代码
jobs:
  data_provenance:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Verify data hash
        run: |
          # 检查数据文件的hash是否匹配
          # 检查模型版本是否与数据版本一致
          # 不一致 → 拒绝
      - name: Reproduce results
        run: |
          # 用当前代码跑一遍,看结果是否与提交的论文数据一致
          # 不一致 → 拒绝

核心逻辑:数据来源可追溯,模型版本可对应。你改了模型,数据没跟着改,CI直接拒绝。

模块5b-项目型:契约对应模块

拦截类型 :接口字段不对齐、类型不匹配、错误码不一致。

场景 :后端说"返回 下划线user_name ",前端写的是 驼峰userName。联调的时候,前端崩了,后端说"我按文档写的"。

实现例

复制代码
jobs:
  contract_check:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Validate OpenAPI spec
        run: |
          # 用 OpenAPI 规范校验接口定义
          # 检查前端代码里的字段名是否与规范一致
          # 不一致 → 拒绝

核心逻辑:接口字段不是"口头约定",是CI里的强制校验。你传 userName,文档要求 user_name,CI直接拒绝。

模块6:异地容灾模块

拦截类型 :主分支被污染,缓存分支还在。

场景:萌新直接往 main 分支 push,CI 没通过也硬上。主分支乱了,没人敢回滚。

实现例

复制代码
jobs:
  protect_main:
    runs-on: ubuntu-latest
    steps:
      - name: Protect main branch
        run: |
          # 检查 main 分支的 CI 状态
          # 如果 CI 未通过,禁止合并
          # 主分支和缓存分支物理隔离

核心逻辑:主分支和缓存分支物理隔离。CI不通过,主分支进不去。你想"硬上",系统不给你这个选项,403是常态。最安全的方式就是没人能改主分支。

模块7:行为记录模块

拦截类型 :不守规矩的人。

场景:萌新force push,覆盖了队友的代码。队友问"谁改的",萌新说"不是我"。

实现例:

复制代码
jobs:
  audit_log:
    runs-on: ubuntu-latest
    steps:
      - name: Log all actions
        run: |
          # 记录每次提交的作者、时间、文件、CI状态
          # 记录每次驳回的原因
          # 记录每次绕过尝试
          # 写入审计日志

核心逻辑:CI不通过,git记录谁、何时、何地、干了什么。剩下的事,就是按规范流程问罪。你不是"在骂人",你是在"在留证据"。


第六部分:老鸟内心精选(CI维权版)

场景1:A组萌新被CI驳回,找老鸟哭诉

A组萌新:"CI驳回了我的提交,这系统是不是针对我?"

D组老鸟内心

针对你?咱家CI又没有根据人来拦截的任务,都是根据部门统一拦截的,它驳回咱们的可能原因全集,是一致的。CI连你是谁都不知道。它只认代码、只认规则、只认时间。你觉得它针对你?你先看看你的commit message写的是什么------"fix"。fix什么?哪里fix?怎么fix?你写遗嘱也这么随便吗?

你说"本地跑得好好的"?你本地连的是localhost,CI连的是测试环境。你当CI是你家路由器啊?

你觉得CI针对你?CI要针对你,你连提交的机会都没有。它现在只是驳回你,已经够客气了。你要是再force push,那就不是驳回,是403。你那时候连权限都没了。

场景2:B组老鸟质疑CI的必要性

B组老鸟:"我二十年没出过事,CI这玩意有必要吗?"

D组老鸟内心

二十年没出过事,是你的运气,不是你的本事。你没出过事,是因为你没遇到过一个force push的队友。你遇到了,你就知道CI近防炮重要了。

你说"我自觉"?自觉是消耗品。你今天自觉,明天自觉,后天累了呢?请假了呢?离职了呢?你是个活生生的人,不是机器。你我都无法保证自己到退休都不会出错,甚至你我都无法保证能不能撑过35。

你说"CI太死板"?CI死板,是因为人太灵活。人灵活到force push,灵活到跳过测试,灵活到封仓后提交。CI不是死板,是它不跟你讲情面。

你说"我用不着"?你用不着,但你带的团队用不着吗?你请假了,你的团队怎么办?你离职了,你的规矩怎么办?

CI不是给你用的,是给你不在的时候用的。

场景3:C组萌新被CI驳回三次后破防

C组萌新:"我都改了三遍了,CI还驳我,我不干了!"

老鸟内心

你不干了?你改了三遍就破防,我改了三十遍都没说话。你改了三遍,是因为你没看日志。你看了日志,就知道错在哪。你没看日志,改一百遍也是白改。日志不是废话,是办案线索,是警察抓小偷的证据。

CI驳你,不是"针对你",是"告诉你错在哪"。你被驳回了三次,说明你有三个地方不对。你改一个地方,重交一次。你改完了,就过了。

你现在说"不干了"?可以。门在那边。但你不是"主动辞职",你是"被系统淘汰"。CI记录摆在那里,你仲裁也赢不了。你不是"不想干",你是"干不了"。

场景4:D组老鸟被问"CI会不会太严"

C组新人:"D哥,CI会不会太严了?一点小错都不让过。"

D组老鸟内心

小错?你说的小错,是commit message写"fix",是URL写localhost,是测试跳过没跑,是force push覆盖别人的劳动成果。这些在你眼里是"小错",在工程里叫"事故前兆"。不是CI太严,是你还没在职场待久。我脾气差,但我不会搞死别人。你能思考CI的重要性,这部分已经强过部分萌新了。

你觉得"一点小错都不让过"是严?那我问你:你开车不系安全带,算不算"小错"?你闯红灯,算不算"小错"?你觉得是"小错",因为你没出过事。出了事,就不是"小错"了,是"重大事故"。

CI不是"严",是不给你"这次算了"的机会你不守规矩,它不跟你商量。你守规矩,它放你过。你守规矩还出事,那是倒霉。你不守规矩出事,那是活该。

场景5:A组萌新被辞退后,回头找老鸟

A组萌新:"我被开了。我就想问一句,我到底错在哪?"

老鸟内心

你错在哪?你错在force push覆盖别人代码的时候,没想过别人。你错在commit message写"fix"的时候,没想过读log的人。你错在跳过测试的时候,没想过用户。你错在封仓后提交的时候,没想过全队。

你不是"不知道",你是"不想知道"。你被CI驳回的时候,你说"CI针对我"。你被辞退的时候,你说"我到底错在哪"。你到现在还在问"我错在哪",说明你根本没意识到自己错了。

你不是"被开了",你是"被系统淘汰了"。CI记录摆在那里,你仲裁也赢不了。你不是"运气不好",你是"不适合工程环境"。

CI不骂人,CI只记录。

老鸟不骂人,老鸟只翻译。

翻译的结果,就是A组的辞退通知书。

第七部分:总结

CI/CD 特点总表

维度 CI/CD 能做什么 CI/CD 不能做什么 本质
拦截 不达标不让合并,403伺候 防不了偷懒的(磨洋工、拖延、假装在忙) 只管行为,不管态度
追溯 记录谁在何时何地干了什么 做不到后续追责(追不追责是管理层的事) 只管记录,不管判决
强制 剥夺你的"决定权",不由你决定 防不住黑客攻击(外部入侵是另一套系统) 只管内部,不管外部
保护 让守规矩的人不"自甘风险" 防不住"家里养不熟的猫"(内鬼防不胜防) 只管规则,不管人心
治理 把"人治"变成"法治" 不能替代人类的进攻性价值(设计架构、定接口、做决策) 只管防御,不管进攻
威慑 巡天雷达,内奸相信它会扫 威慑力取决于内奸是否"相信" 只管心理,不管事实
时间 时间铡刀,封仓后不准提交 防不了"提前交卷但交的是烂卷" 只管时间,不管质量
权限 越权封锁,你只能动你的 防不了"有权限但乱用权限" 只管边界,不管意图
环境 自动编译,本地跑通不算数 防不了"CI跑通了但生产环境不一样" 只管CI环境,不管生产环境
质量 成品校验,没测过不让合 防不了"测试写了但没覆盖边界" 只管测没测,不管测得全不全
科研 追根溯源,数据来源可验证 防不了"数据是真的但模型是错的" 只管来源,不管结论
项目 契约对应,字段不对齐不让合 防不了"字段对齐了但逻辑是错的" 只管接口,不管实现
容灾 异地容灾,主分支和缓存分支隔离 防不了"两个分支都错了" 只管隔离,不管正确
行为 行为记录,谁干了什么全留痕 防不了"记录了但没人看" 只管留痕,不管阅读

最后几句人话

CICD不是万能的,但不加这玩意基本等于赌博。

CICD防得了搞事的,防不了偷懒的。

CICD能记录搞事证据,做不到后续追责。

CICD能防住家里养不熟的猫,但防不住黑客攻击。

我知道此时那些巨婴读者们想在评论区骂我,但我不在乎。我凭什么写?凭我自己真的碰见过多次事故。真话说出来必然会刺痛部分群体,但这篇不是毫无意义的引战,是思辨。

我拆的是一种思维类型,而不是一个个体,不存在所谓人身攻击。

真正的工程环境,是不会跟你讲情绪的。

工程,不是只有结果,但最看结果,最看你能不能按要求办事。

不能?那就不得不上强制措施了。

不是老板对你太严格,而是你没能做到让老板放心。

成年人,尤其是职场的成年人,要有责任意识。

你没证明自己值得别人合作,那你最好别上这个谈判桌。

......

除非你不后悔。

附:第十六期自测题

第一题:你的团队有没有CI?

  • 有,且是强制门禁(不通过不能合并)
  • 有,但不是强制门禁(可以手动绕过)
  • 没有,靠人盯人
  • 不知道什么是CI

如果你的答案是后两个,你就是在赌。

第二题:你的CI拦住了什么?

  • 时间:封仓后不能提交
  • 权限:不能动别人的模块
  • 环境:本地跑通不等于CI跑通
  • 测试:没测过不让合
  • 数据:模型改了,数据必须跟着改
  • 接口:字段不对齐不让合
  • 容灾:主分支和缓存分支隔离
  • 行为:谁在何时何地干了什么,全记录

拦不住的,就是你正在赌的。

第三题:你在CI里,是哪种人?

  • A组:没有CI,没有规矩,出事就甩锅
  • B组:没有CI,靠自觉,人走规矩散
  • C组:有CI,被系统管,但开始学规矩了
  • D组:有CI,自己守规矩,还能建规矩

A组被淘汰,B组在赌,C组在学,D组在跑。

第四题:你敢不敢回答这个问题?

你上次force push是什么时候?

  • 我从来不用force push,因为知道后果
  • 我用过,但我知道错了
  • 我用过,但我觉得没什么
  • force push是什么?

如果你选第三个,你就是评论区要骂我的那个人。

如果你选第四个,你就是A组。

第五题:最后一题

你后悔吗?

  • 不后悔,我会继续
  • 后悔了,但不知道怎么办
  • 后悔了,我已经开始改了
  • 我不知道你在问什么

后悔了不知道怎么办的,从第一题重新做。

后悔了已经开始改的,你正在从C组走到D组。

不后悔的,评论区见。

不知道我在问什么的,你就是A组。