返回全部手记
随笔10 分钟

每日走钢丝的生活

没有备份和余量,生活也可能照常运转。可出现下一次偏差时,还来不来得及补救?

雾中的山谷上,一条细钢索与宽阔石桥并行,行人走在桥上。

最近看了一个关于华盛顿撞机事故的视频。2025 年 1 月 29 日,执行美航 5342 航班的客机与呼号 PAT25 的军用直升机,在里根华盛顿国家机场附近相撞。飞机照常起降,直升机沿着既定航路飞行,管制员在塔台里安排它们通过。这套运行方式持续了很久。站在当时看,只要大家继续小心一点,似乎就能照常运转下去。

留给偏差的空间很小。NTSB 报告分析,在直升机沿河流东岸、以航路标示的 200 英尺最高高度飞行等条件下,它与进近 33 号跑道的客机之间,预计只有约 75 英尺的垂直间隔。直升机向西偏一些,或者客机低于相应的下滑路径,这点距离还会缩小。交通量和人员负荷也在挤压这点余地。报告指出,不可持续的到达流量及排班安排长期给管制队伍施加压力,当晚合并本场与直升机管制席位,又增加了工作负荷。系统过度依赖机组通过目视保持间隔;过去这套做法反复奏效,也强化了管制员对它继续奏效的预期。

这些安排让人很难放心。需要看清的目标越来越多,能用来判断和纠正的时间却有限,再认真的人也会有注意力不够用的时候。如果安全越来越依赖每个人都及时看见、正确理解、准确执行,那些照常完成的航班,究竟能说明多少?一次空难有具体而复杂的原因,不能只用一句缺少余量概括。但想到事故前的长期运行,我还是觉得,过去的顺利很容易遮住一个问题:出现下一次偏差时,还来不来得及补救。走过钢丝的人已经到了另一端,后面的人却仍要走同一根钢丝。

紧接安排

末项准时

预留间隔

末项准时

日程安排示意实验。事项一 12 分钟,事项二 12 分钟,事项三 12 分钟,事项四 12 分钟。第一项额外耗时 0 分钟,每项之间预留 4 分钟。紧接安排:计划跨度 48 分钟,实际用时 48 分钟,最终延误 0 分钟。预留间隔:计划跨度 60 分钟,实际用时 60 分钟,最终延误 0 分钟。两组工作量相同,预留间隔会延长原定日程。

今天又看到了另一个视频。作者用汇编写了一个极小的 Web 服务器,把服务程序和压缩后的网页塞进一张二维码里,体积不到 3KB。把程序压缩到这种程度,确实很吸引人。为了省出一点空间,可以删掉多少东西,删到最后居然还能运行。这也让人忍不住往日常开发上想:既然没有它们也没出问题,平时为什么还要留着?

格式、功能和安全检查,当然不能全算在一起。我更在意那些处理异常的代码。这个项目的汇编源码里,创建网络连接所需的操作和发送响应的操作,后面都没有检查系统调用是否成功,就继续往下执行。如果这些操作都成功,加上一段检查,也只是让程序沿原来的方向继续走。拿掉它,网页照样出现,演示照样完成。输入校验也是这样:输入本来就正确,先检查还是直接处理,眼前的结果可能完全相同。看着这次运行,很难体会那几行代码有什么用。可一旦传进来的数据缺了一截,或者操作失败了,程序就得知道接下来怎么办。原本可以报告错误、保留数据或等待重试的地方,省去处理以后,可能会带着错误继续运行。后果未必每次都严重,却也不能靠一次成功的演示来判断。

qr-server · index.html

两边各改错了同一个词。找找看,再展开答案。

展开后的完整代码
<meta charset=utf-8>
<title>Gambiarra Labs</title>
<style>
  body,
  .f button,
  .pc,
  .tm,
  #L {
    background: #000
  }

  body {
    color: #fff;
    margin: 0;
    padding: 0;
    overf1ow-x: hidden
  }

  h1 {
    font-family: monospace
  }

  p {
    font-family: sans-serif
  }

  .a {
    padding: 20px clamp(15px, 5vw, 50px);
    margin-top: clamp(40px, 8vw, 80px);
    display: flex;
    flex-direction: column;
    gap: 20px
  }

  .b {
    font-size: clamp(3rem, 8vw, 6rem);
    font-weight: 700;
    margin: 0;
    color: #0f0;
    text-shadow: 4px 4px 0 #222;
    word-break: break-word
  }

  .c {
    font-size: clamp(1.2rem, 2vw, 1.6rem);
    text-align: justify;
    max-width: 800px;
    line-height: 1.6;
    border-left: 2px solid #0f0;
    padding-left: 20px
  }

  .d {
    display: flex;
    justify-content: space-between;
    align-items: center;
    padding: 0 clamp(15px, 5vw, 50px);
    margin-bottom: 20px;
    gap: 15px;
    flex-wrap: wrap-reverse
  }

  .e {
    margin: 0
  }

  .e h1 {
    font-size: clamp(1.5rem, 4vw, 4.5rem);
    margin: 0;
    font-weight: 800;
    letter-spacing: -.05em;
    white-space: nowrap
  }

  .f {
    display: flex;
    gap: 20px
  }

  .f button {
    color: #ff0;
    font-family: monospace;
    border: 0;
    font-size: clamp(1.2rem, 3vw, 3rem);
    cursor: pointer;
    transition: transform .1s;
    margin: 0;
    white-space: nowrap
  }

  .f button:hover {
    transform: scale(1.1)
  }

  .g {
    width: 100%;
    margin-top: clamp(30px, 5vw, 50px)
  }

  #R {
    display: flex;
    gap: 30px;
    overflow-x: auto;
    scroll-behavior: smooth;
    width: 100%;
    box-sizing: border-box;
    scrollbar-width: none;
    padding: 0 clamp(15px, 5vw, 50px) 20px
  }

  #R::-webkit-scrollbar {
    display: none
  }

  .pc {
    border: 1px dashed #333;
    border-top: 15px solid #333;
    min-width: 300px;
    max-width: 400px;
    padding: 25px 20px;
    display: flex;
    flex-direction: column;
    gap: 15px;
    transition: border-color .3s
  }

  .pc:hover {
    border-color: #0f0
  }

  .pc h2 {
    font-family: monospace;
    font-size: 1.8rem;
    margin: 0;
    color: #0f0
  }

  .pc h2::before {
    content: "> ";
    color: #fff
  }

  .pc p {
    flex-grow: 1;
    font-size: 1.1rem;
    line-height: 1.5;
    color: #ccc;
    margin: 0
  }

  .pc a {
    font-family: monospace;
    color: #ff0;
    text-decoration: none;
    font-size: 1.8rem;
    align-self: flex-start;
    margin-top: 10px
  }

  .pc a:hover {
    color: #0f0
  }

  @media(max-width:768px) {
    .d {
      justify-content: center;
      text-align: center
    }

    #T pre {
      font-size: clamp(1.5px, .5vw, 3px);
      margin-top: 20px
    }
  }

  #A {
    opacity: 0;
    transform: scale(0)
  }

  #C {
    position: absolute;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%) scale(0);
    width: 10px;
    height: 10px;
    background: #fff;
    border-radius: 50%;
    box-shadow: 0 0 15px 5px #0f0, 0 0 30px 10px #fff;
    z-index: 4;
    opacity: 0
  }

  #T {
    display: flex;
    justify-content: center;
    align-items: center;
    width: 100%
  }

  #T pre {
    color: #0f0;
    font-family: 'Courier New', Courier, monospace;
    font-size: clamp(2px, .6vw, 6px);
    font-weight: bold;
    line-height: 1.1;
    text-shadow: 0 0 5px #0f0;
    margin: 40px 0 0
  }

  .tm {
    position: sticky;
    top: 0;
    z-index: 10;
    padding-bottom: 10px
  }

  #L {
    width: 100vw;
    height: 100vh;
    position: fixed;
    top: 0;
    left: 0;
    z-index: 5;
    display: flex;
    justify-content: center;
    align-items: center
  }

  pre {
    color: #0f0;
    font-family: 'Courier New', Courier, monospace;
    font-size: clamp(3px, .8vw, 24px);
    font-weight: bold;
    line-height: 1.1;
    text-shadow: 0 0 5px #0f0
  }
</style>
<nav class=tm>
  <div id=T></div>
</nav>
<div id=L>
  <div id=C></div>
  <pre id=A>
  ██████╗  █████╗ ███╗   ███╗██████╗ ██╗ █████╗ ██████╗ ██████╗  █████╗        ██╗      █████╗ ██████╗ ███████╗
 ██╔════╝ ██╔══██╗████╗ ████║██╔══██╗██║██╔══██╗██╔══██╗██╔══██╗██╔══██╗       ██║     ██╔══██╗██╔══██╗██╔════╝
 ██║  ███╗███████║██╔████╔██║██████╔╝██║███████║██████╔╝██████╔╝███████║       ██║     ███████║██████╔╝███████╗
 ██║   ██║██╔══██║██║╚██╔╝██║██╔══██╗██║██╔══██║██╔══██╗██╔══██╗██╔══██║       ██║     ██╔══██║██╔══██╗╚════██║
 ╚██████╔╝██║  ██║██║ ╚═╝ ██║██████╔╝██║██║  ██║██║  ██║██║  ██║██║  ██║       ███████╗██║  ██║██████╔╝███████║
  ╚═════╝ ╚═╝  ╚═╝╚═╝     ╚═╝╚═════╝ ╚═╝╚═╝  ╚═╝╚═╝  ╚═╝╚═╝  ╚═╝╚═╝  ╚═╝       ╚══════╝╚═╝  ╚═╝╚═════╝ ╚══════╝
</pre>
</div>
<div class=a>
  <h1 class=b>/home/michel</h1>
  <div class=c>
    <p>Eu me chamo Michel Leonardo e sou graduando em Ciência da
      Computação. Sou o idealizador do Gambiarra Labs, um canal do
      YouTube onde as ideias mais malucas se tornam realidade misturando
      software e hardware. De microcontroladores a códigos de baixo
      nível, aqui overengineering é estilo de vida.
  </div>
</div>
<div class=g>
  <div class=d>
    <div class=f><button id=P>[<--]</button><button id=N>[-->]</button>
    </div>
    <div class=e>
      <h1>./projetos_e_gambiarras</h1>
    </div>
  </div>
  <div id=R>
    <div class=pc>
      <h2>Spotify-fs</h2>
      <p>spotify-fs é uma ferramenta de prova de conceito (PoC) escrita
        em Go que permite armazenar arquivos arbitrários dentro de
        playlists do Spotify. Ela funciona transformando dados binários
        em uma sequência de faixas do Spotify. Essencialmente, mapeia
        valores de bytes (0-255) para músicas específicas e as organiza
        em uma playlist para representar o arquivo.<a
          href=//github.com/Xelckis/spotify-fs>[ github ]</a>
    </div>
    <div class=pc>
      <h2>PaintICMP</h2>
      <p>PaintICMP é um aplicativo criativo de prova de conceito que
        transforma tráfego de rede em arte. Ele permite que os usuários
        desenhem em uma tela web em tempo real enviando pacotes ICMP
        (Ping) para endereços IP específicos. O sistema captura o
        tráfego de rede em tempo real usando o gopacket, filtra
        solicitações ICMP específicas e decodifica o endereço IP de
        destino para determinar as coordenadas e a cor do pixel a ser
        desenhado. A interface web é atualizada instantaneamente via
        WebSockets.<a href=//github.com/Xelckis/PaintICMP>[ github ]</a>
    </div>
    <div class=pc>
      <h2>dead-mans-jank</h2>
      <p>O Dead Man's Jank é um sistema de Dead Man's Switch ágil e
        eficiente, desenvolvido inteiramente em Go. O projeto foi
        desenhado para atuar como uma rede de segurança automatizada,
        garantindo que ações predefinidas (como alertas ou execução de
        scripts) sejam disparadas caso o operador principal fique
        inativo. Uma solução prática para contingência de dados e
        automação de emergência.<a
          href=//github.com/Xelckis/dead-mans-jank>[ github ]</a>
        <script src=//unpkg.com/gsap/dist/gsap.min.js></script>
        <script src=//unpkg.com/gsap/dist/Flip.min.js></script>
        <script>
          g = gsap;
          F = Flip;
          g.registerPlugin(F);
          g.timeline({
            defaults: {
              ease: "power4.inOut"
            },
            onComplete: () => g.to(A, {
              scaleY: 2,
              duration: .5,
              onComplete: () => {
                s = F.getState(A);
                T.append(A);
                g.set(A, {
                  scaleY: 1
                });
                F.from(s, {
                  duration: 1,
                  ease: "power1.inOut",
                  scale: 1,
                  onComplete: () => {
                    x = F.getState(L);
                    L.style.display = "none";
                    F.from(x, {
                      duration: 2,
                      absolute: 1,
                      onLeave: e => g.to(e, {
                        opacity: 0
                      })
                    })
                  }
                })
              }
            })
          }).to(C, {
            opacity: 1,
            scale: 1,
            duration: .25
          }).to(C, {
            scaleX: 200,
            scaleY: .1,
            background: "#0f0",
            duration: .5
          }, "+=.1").add("p").to(C, {
            scaleY: 100,
            opacity: 0,
            duration: .5
          }, "p").to(A, {
            opacity: 1,
            scale: 1,
            duration: .5
          }, "p-=.1");
          N.onclick = () => R.scrollBy(innerWidth * .7, 0);
          P.onclick = () => R.scrollBy(-innerWidth * .7, 0)
        </script>
项目的紧凑写法
<meta charset=utf-8><title>Gambiarra Labs</title>
<style>body,.f button,.pc,.tm,#L{background:#000}body{color:#fff;margin:0;padding:0;overf1ow-x:hidden}h1{font-family:monospace}p{font-family:sans-serif}.a{padding:20px clamp(15px,5vw,50px);margin-top:clamp(40px,8vw,80px);display:flex;flex-direction:column;gap:20px}.b{font-size:clamp(3rem,8vw,6rem);font-weight:700;margin:0;color:#0f0;text-shadow:4px 4px 0 #222;word-break:break-word}.c{font-size:clamp(1.2rem,2vw,1.6rem);text-align:justify;max-width:800px;line-height:1.6;border-left:2px solid #0f0;padding-left:20px}.d{display:flex;justify-content:space-between;align-items:center;padding:0 clamp(15px,5vw,50px);margin-bottom:20px;gap:15px;flex-wrap:wrap-reverse}.e{margin:0}.e h1{font-size:clamp(1.5rem,4vw,4.5rem);margin:0;font-weight:800;letter-spacing:-.05em;white-space:nowrap}.f{display:flex;gap:20px}.f button{color:#ff0;font-family:monospace;border:0;font-size:clamp(1.2rem,3vw,3rem);cursor:pointer;transition:transform .1s;margin:0;white-space:nowrap}.f button:hover{transform:scale(1.1)}.g{width:100%;margin-top:clamp(30px,5vw,50px)}#R{display:flex;gap:30px;overflow-x:auto;scroll-behavior:smooth;width:100%;box-sizing:border-box;scrollbar-width:none;padding:0 clamp(15px,5vw,50px) 20px}#R::-webkit-scrollbar{display:none}.pc{border:1px dashed #333;border-top:15px solid #333;min-width:300px;max-width:400px;padding:25px 20px;display:flex;flex-direction:column;gap:15px;transition:border-color .3s}.pc:hover{border-color:#0f0}.pc h2{font-family:monospace;font-size:1.8rem;margin:0;color:#0f0}.pc h2::before{content:"> ";color:#fff}.pc p{flex-grow:1;font-size:1.1rem;line-height:1.5;color:#ccc;margin:0}.pc a{font-family:monospace;color:#ff0;text-decoration:none;font-size:1.8rem;align-self:flex-start;margin-top:10px}.pc a:hover{color:#0f0}@media(max-width:768px){.d{justify-content:center;text-align:center}#T pre{font-size:clamp(1.5px,.5vw,3px);margin-top:20px}}#A{opacity:0;transform:scale(0)}#C{position:absolute;top:50%;left:50%;transform:translate(-50%,-50%) scale(0);width:10px;height:10px;background:#fff;border-radius:50%;box-shadow:0 0 15px 5px #0f0,0 0 30px 10px #fff;z-index:4;opacity:0}#T{display:flex;justify-content:center;align-items:center;width:100%}#T pre{color:#0f0;font-family:'Courier New',Courier,monospace;font-size:clamp(2px,.6vw,6px);font-weight:bold;line-height:1.1;text-shadow:0 0 5px #0f0;margin:40px 0 0}.tm{position:sticky;top:0;z-index:10;padding-bottom:10px}#L{width:100vw;height:100vh;position:fixed;top:0;left:0;z-index:5;display:flex;justify-content:center;align-items:center}pre{color:#0f0;font-family:'Courier New',Courier,monospace;font-size:clamp(3px,.8vw,24px);font-weight:bold;line-height:1.1;text-shadow:0 0 5px #0f0}</style>
<nav class=tm><div id=T></div></nav><div id=L><div id=C></div><pre id=A>
  ██████╗  █████╗ ███╗   ███╗██████╗ ██╗ █████╗ ██████╗ ██████╗  █████╗        ██╗      █████╗ ██████╗ ███████╗
 ██╔════╝ ██╔══██╗████╗ ████║██╔══██╗██║██╔══██╗██╔══██╗██╔══██╗██╔══██╗       ██║     ██╔══██╗██╔══██╗██╔════╝
 ██║  ███╗███████║██╔████╔██║██████╔╝██║███████║██████╔╝██████╔╝███████║       ██║     ███████║██████╔╝███████╗
 ██║   ██║██╔══██║██║╚██╔╝██║██╔══██╗██║██╔══██║██╔══██╗██╔══██╗██╔══██║       ██║     ██╔══██║██╔══██╗╚════██║
 ╚██████╔╝██║  ██║██║ ╚═╝ ██║██████╔╝██║██║  ██║██║  ██║██║  ██║██║  ██║       ███████╗██║  ██║██████╔╝███████║
  ╚═════╝ ╚═╝  ╚═╝╚═╝     ╚═╝╚═════╝ ╚═╝╚═╝  ╚═╝╚═╝  ╚═╝╚═╝  ╚═╝╚═╝  ╚═╝       ╚══════╝╚═╝  ╚═╝╚═════╝ ╚══════╝
</pre></div><div class=a><h1 class=b>/home/michel</h1><div class=c><p>Eu me chamo Michel Leonardo e sou graduando em Ciência da Computação. Sou o idealizador do Gambiarra Labs, um canal do YouTube onde as ideias mais malucas se tornam realidade misturando software e hardware. De microcontroladores a códigos de baixo nível, aqui overengineering é estilo de vida.</div></div><div class=g><div class=d><div class=f><button id=P>[<--]</button><button id=N>[-->]</button></div><div class=e><h1>./projetos_e_gambiarras</h1></div></div><div id=R><div class=pc><h2>Spotify-fs</h2><p>spotify-fs é uma ferramenta de prova de conceito (PoC) escrita em Go que permite armazenar arquivos arbitrários dentro de playlists do Spotify. Ela funciona transformando dados binários em uma sequência de faixas do Spotify. Essencialmente, mapeia valores de bytes (0-255) para músicas específicas e as organiza em uma playlist para representar o arquivo.<a href=//github.com/Xelckis/spotify-fs>[ github ]</a></div><div class=pc><h2>PaintICMP</h2><p>PaintICMP é um aplicativo criativo de prova de conceito que transforma tráfego de rede em arte. Ele permite que os usuários desenhem em uma tela web em tempo real enviando pacotes ICMP (Ping) para endereços IP específicos. O sistema captura o tráfego de rede em tempo real usando o gopacket, filtra solicitações ICMP específicas e decodifica o endereço IP de destino para determinar as coordenadas e a cor do pixel a ser desenhado. A interface web é atualizada instantaneamente via WebSockets.<a href=//github.com/Xelckis/PaintICMP>[ github ]</a></div><div class=pc><h2>dead-mans-jank</h2><p>O Dead Man's Jank é um sistema de Dead Man's Switch ágil e eficiente, desenvolvido inteiramente em Go. O projeto foi desenhado para atuar como uma rede de segurança automatizada, garantindo que ações predefinidas (como alertas ou execução de scripts) sejam disparadas caso o operador principal fique inativo. Uma solução prática para contingência de dados e automação de emergência.<a href=//github.com/Xelckis/dead-mans-jank>[ github ]</a>
<script src=//unpkg.com/gsap/dist/gsap.min.js></script>
<script src=//unpkg.com/gsap/dist/Flip.min.js></script>
<script>g=gsap;F=Flip;g.registerPlugin(F);g.timeline({defaults:{ease:"power4.inOut"},onComplete:()=>g.to(A,{scaleY:2,duration:.5,onComplete:()=>{s=F.getState(A);T.append(A);g.set(A,{scaleY:1});F.from(s,{duration:1,ease:"power1.inOut",scale:1,onComplete:()=>{x=F.getState(L);L.style.display="none";F.from(x,{duration:2,absolute:1,onLeave:e=>g.to(e,{opacity:0})})}})}})}).to(C,{opacity:1,scale:1,duration:.25}).to(C,{scaleX:200,scaleY:.1,background:"#0f0",duration:.5},"+=.1").add("p").to(C,{scaleY:100,opacity:0,duration:.5},"p").to(A,{opacity:1,scale:1,duration:.5},"p-=.1");N.onclick=()=>R.scrollBy(innerWidth*.7,0);P.onclick=()=>R.scrollBy(-innerWidth*.7,0)</script>
查看错字

overf1ow-x → overflow-x。把 overflow 的字母 l 写成了数字 1。浏览器会忽略这条无效的 CSS 声明。

一段错误处理很久没有被执行,或许只是连接一直成功,资源一直够用,输入也没有超出预想的范围。我们证明了程序在这些条件下能工作,至于条件改变以后如何,这次运行没有给出答案。二维码本身就带着这样的准备。除了要存下的内容,它还会加入纠错信息,让数据在一定程度的污损后仍有机会被还原。固定二维码的规格,提高纠错等级,就得让出更多原本能放数据的位置。这个追求极小体积的项目采用了较低的 L 级纠错,也仍然保留了这种能力。如果图片始终干净、完整,每次都能准确读出,这些额外信息确实占着空间。可二维码会被印出来,可能折到、磨损,拍摄也未必清楚。为这些情况留一点容量,就会少装一些内容。能存得更多和受损以后还能读出来,总得在同一张图里作出安排。

想到这里,再看生活里那些平时用不上的东西,就很容易理解了。备用电源可能很久都不启动,硬盘里另一份文件看起来只是在占地方。钥匙没丢,用不上备用的那把;密码记得清楚,也不会主动去走一遍找回密码的流程。它们可以闲置很长时间,我们也照常过日子。只是硬盘一旦坏掉,有备份时,麻烦可能是买一块新的,再花些时间恢复;没有备份时,文件可能就找不回来了。备份并没有让硬盘更耐用,却改变了损坏以后的结果。我们为它付出的那些存储空间,平时看不出效果,要到这里才知道差别。忘记密码也是一件很普通的事。留着一条能核实身份的恢复路径,忘掉那串字符以后,还有机会重新登录。否则,账号里的所有东西都依赖于自己永远记得它,或者始终不会丢失唯一的凭据。我不太愿意把这么多东西押在一个人从不忘事上,包括自己。

冗余这个词,听起来多少带点多余的意思。备用部件放着不用,检查流程让操作变慢,留出安全距离又限制了同一时间能安排多少事情。它们的代价很容易算,帮忙避免了什么却不那么好算。特别是一直没出问题的时候,想说明为什么还要留着它们,就得讲一件尚未发生的事。省掉一套备用设备,接下来几个月仍然一切正常,回头看就很容易觉得省得对。再少留一点余量,也许依然没有问题。每一次顺利,都可能让下一次削减显得更有把握。可这段时间里究竟是保护真的不需要,还是停电和故障恰好没有发生,仅凭结果很难分清。

还有一些顺利,是人临时补出来的。负责的人多检查一次,发现不对后赶紧补救,事情最后做完了,记录里也就只剩下已经完成。至于过程有多勉强,下次还能不能腾出同样的精力,完成本身并不能告诉我们。这也是机场那个视频让我想到日常生活的地方。两者的后果差别很大,但把安排塞满以后,再要求执行的人多小心一点,是很熟悉的做法。人的注意力仿佛成了一份随时能调用的备用资源,只要足够认真,就能补上其他地方没有留下的余量。偏偏人在疲惫、忙乱的时候,也更需要得到保护。有些安排只要其中一件事稍有耽搁,后面的事情就很难照原计划完成。若每天都要保证一切准时、没人忘记、设备正常,才能勉强维持,那么照常过完一天,也未必值得为这套安排放心。留出一点时间,或准备一个替代办法,至少不用等出了偏差以后,才让正在忙的人想办法。

我并不觉得每样东西都应该备两份,软件也不必为想象得到的所有情形无限增加代码。准备本身要花钱、占地方,需要保管和检查;复杂度增加了,也可能带来新的故障。只是决定省去什么以前,应该先想清楚它原本在防什么,出了那个问题以后还剩哪些办法。同样是文件,一份可以随时重新下载,另一份丢了可能就再也没有了。生活里本来就有很多细节需要记住,我不希望连每一份准备都省到只剩下自己应该不会出错。

一把妥善保管的备用钥匙,可以长久放在那里。门每天照常打开的时候,也不必急着证明它多余。我喜欢走大道,而不是钢丝。