選擇,會加速你的開發嗎? 三分鐘斷捨離,讓每天都早點下班

me
林彥成
2023-10-13 | 5 min.
文章目錄
  1. 1. 工作的選擇
  2. 2. 環境的選擇
  3. 3. 工具的選擇
  4. 4. 程式撰寫的選擇
    1. 4.1. 更少的打字時間
    2. 4.2. 更少的除錯與測試時間
    3. 4.3. 更少的閱讀時間
    4. 4.4. 重構元件範例

小編在前陣子參加了 AWS 社群日,體驗了怎麼用英雄聯盟的資料來做模型的訓練和機器學習,並透過模型來為遊戲結果提供預測或建議。

對於 Python 菜鳥如我竟然可以在短短幾分鐘內使用 Jupyter Notebook 來探索和分析真實英雄聯盟對戰數據,當下覺得非常驚訝。

在這樣短暫的體驗下 AI 在過程中給了不少協助,舉個正在頻繁發生的例子來說,在 GPT 在跟 Chat 一起出現之後

  • Copilot 的出現也讓小編的同事在撰寫單元測試省下很多時間
  • 給圖片能夠產生出程式碼的 Figma
  • 聊天機器人帶來的新零售和電子商務
  • 生成式藝術

接下來的下個時代,快速跟著世代變化做選擇似乎變得越來越需要。

在軟體工程領域,我們經常需要決定學習哪些新技術、參與哪些專案以及投入多少時間和精力,這些選擇將直接影響我們的職業生涯和生活。

這幾年的生活和工作發展下來,體驗到的是

工作和生活就是我們過去所做過的選擇的總和

當你重回當初的模樣,回頭過來看看曾經瘋狂、荒謬、驚奇、平靜、難忘的生活,最難遺忘的會是什麼。

工作的選擇

想來談談工作的選擇,資本主義社會給了我們穩定的金錢和標籤,而透過金錢和標籤請我們進行角色扮演。

在角色扮演的過程中,各種決定和取捨總會需要評估,其中只存在一個問題,那就是工作對你而言是什麼?選擇工作對人生價值有何意義?

在工作的選擇上可以粗分成三大類

  • 核心工作: 最重要、最有價值的任務和職責
    • 開發核心產品功能
    • 解決關鍵技術問題
    • 提供高品質程式碼
    • 協作和溝通
  • 專案工作
    • 短期專案
    • 臨時任務
    • 緊急修復
    • 日常例行性項目
  • 發展性工作
    • 持續學習和專業發展

這三類大致構成了工作的樣貌,而金錢本身讓你為了這些犧牲了什麼?交換了什麼?舉個例子來說,最害人不淺的一句話大概就是你的工作很穩,快去買棟房子吧!這句話背後的交換隱含了些什麼?

買了房子之後,公司和老闆給了足夠的金錢,兌換未來 30 年不間斷的專案工作,這會是你想要的人生嗎?生活選擇該是一道道單選題,或是該持續反思後才選擇往前進?

環境的選擇

人際關係和工作環境也可能會是有毒的,生活中其實最該選擇和斷捨離的會是人際關係,小時候常常聽到人脈,但其實多認識一個人其實不會真的多什麼資產。

  • 融入群體有必要嗎? 如果那個群體有毒,那不就會慢性中毒?
  • 為什麼無法過著自己想要的生活,跟大家一樣到底有什麼好?
  • 團隊對我的工作必要嗎? 團隊能幫助我接近理想生活嗎?

充滿著追求更多,最終會不會就過著被被垃圾資訊、物質淹沒的生活。

底下是小編請 Chat GPT 列出的一些警訊

  • 不正常的壓力和負面情感
  • 控制和操控
  • 不健康的競爭
  • 缺乏支持和合作
  • 缺乏職涯發展
  • 不透明和不公平
  • 虛偽和欺騙
  • 工作生活無法平衡

工具的選擇

工具的選擇來提兩種最可以直接有感增加開發者體驗的工具

  • 自動化和程式碼品質檢查工具
    • ESlint、SonarLint: 自動檢查程式碼,並且建議最佳實踐
    • commit lint: 自動檢查 commit 的 message
    • Prettier: 確保一致的風格
  • 持續整合和持續交付(CI/CD)工具,以實現自動化的測試、部署和整合,這些工具可幫助減少人工造成的風險並把開發流程自動化
    • Jenkins: 老牌好用的 GUI 工具但其實也支援用程式碼進行設定
    • Travis CI、Circle CI: 第三方的老牌服務,透過 YAML 來設定
    • GitLab CI、GitHub Actions、Azure DevOps: 版控工具原生內建的工具,算是近期推出的全家餐解決方案

程式撰寫的選擇

以程式碼撰寫來說,主要會有三種加速的目標

  1. 更少的打字時間
  2. 更少的除錯與測試時間
  3. 更少的閱讀時間

接下來的內容主要來翻譯 Double your react coding speed with this simple trick 這篇文章,會從剛剛提到的三個角度來切入 React 開發效率。

更少的打字時間

要讓打字時間縮短,大致上分為兩個方向:

  1. DRY 增加共用的程式碼: 大元件拆分成小且可重用的 hooks 或元件

    • 資料處理邏輯獨立
    • 元件只處理顯示
    • 簡化輸入參數 (props)
  2. 善用工具

    • Snippets: 編輯器外掛協助自動完成
    • Auto Import: 編輯器外掛協助路徑輸入
    • prettier: 編輯器自動格式化

更少的除錯與測試時間

通常找到錯誤的流程要先把 App 用開發模式跑起來後,操作元件重現錯誤並查看相關錯誤訊息,修復後重試。在開發階段如果要避免低階錯誤,可分成兩個方向

  1. 撰寫測試並導入 CI/CD,在流程中就靠機器提早幫我們發現問題
  2. 善用工具,在開發階段透過 eslint、props type check、TypeScript 來避免低階問題

更少的閱讀時間

開發者最常花時間在看懂程式碼而非撰寫,通常好的程式都有容易被修改、容易發現問題的優點,那在撰寫上列出幾個我覺得需要改掉的壞習慣:

  • 程式碼太多暗號
  • 類似功能卻散在各處
  • 太長的函式,需要花時間才能了解
  • 使用上需要滿滿的參數卻又未分類
  • 太多布林值狀態
// 程式碼太多暗號
value[0][index] = sum;

// 滿滿的參數卻又未分類
<Grid
  data={gridData}
  pagination={false}
  autoSize={true}
  enableSort={true}
  sortOrder="desc"
  disableSelection={true}
  infiniteScroll={true}
  ...
/>

const options = {
  pagination: false,
  autoSize: true,
  enableSort: true,
  sortOrder: 'desc',
  disableSelection: true,
  infiniteScroll: true,
  ...
}

<Grid
  data={gridData}
  options={options}
/>

// 太多布林值狀態

const [isLoading, setIsLoading] = useState(false)
const [isFinished, setIsFinished] = useState(false)
const [hasError, setHasError] = useState(false)

const fetchSomething = () => {
  setIsLoading(true)

  fetch(url)
    .then(() => {
      setIsLoading(false)
      setIsFinished(true)
    })
    .catch(() => {
      setHasError(true)
    })
}

const [state, setState] = useState('idle')

const fetchSomething = () => {
  setState('loading')

  fetch(url)
    .then(() => {
      setState('finished')
    })
    .catch(() => {
      setState('error')
    })
}

重構元件範例

元件優化的方向與過程,最主要還是拆解成可重複使用的小單位,像是運用 render props 或是 HOC 甚至是寫成 Custom hook,對岸維護的 ahook 功能就非常多樣。常見可以優化的部分我覺得分以下三個部分:

  1. 打 API 的寫法
    • 初階: 在元件裡面抓資料
    • 進階: 透過 redux 這類工具統一資料處理邏輯
    • 進階: 寫一個 hook 或是使用 react-query
  2. 錯誤處理
    • 初階: 直接在打 API 的 function 中寫判斷
    • 進階: ErrorBoundary 處理或是 axios interceptor
  3. 樣式檔的優化
    • 初階: CSS
    • 進階: Sass or SCSS
    • 進階: CSS-in-JS
    • 進階: Atomic-css

最開始的元件撰寫通常會長成下面這樣,在同樣一個元件裡面放了狀態,打 API 抓資料的相關程式,還有顯示的部分,好處當然就是直觀也不會說不好維護,但當這樣的元件有 50 個的時候,打 API 的方式要修改時,也許就會懷疑人生?

import React, { useEffect, useState } from "react";

import AddModal from "../components/AddModal";
import LoadingIndicator from "../components/LoadingIndicator";
import BrowserItem from "../components/BrowserItem";

import colors from "../config/colors";

function Browsers() {
  const URL = "https://google.com/myData.json";

  const [loading, setLoading] = useState(true);
  const [browsers, setBrowsers] = useState([]);

  const [modalVisible, setModalVisible] = useState(false);
  const [description, setDescription] = useState("");

  const changeDescription = (description) => {
    setDescription(description);
    setModalVisible(!modalVisible);
  };

  const changeOpacity = () => {
    setModalVisible(!modalVisible);
  };

  useEffect(() => {
    fetch(URL)
      .then((response) => response.json())
      .then((responseJson) => {
        return responseJson.Browsers;
      })
      .then((browsers) => {
        setBrowsers(browsers);
        // console.log(browsers)
        setLoading(false);
      })
      .catch((error) => {
        console.log(error);
      })
      .finally(() => setLoading(false));
  }, []);

  return (
    <>
      {loading ? (
        <LoadingIndicator />
      ) : (
        <>
          <AddModal
            modalVisible={modalVisible}
            changeOpacity={() => changeOpacity()}
            description={description}
          />
          <List
            data={browsers}
            keyExtractor={(browser) => browser.fullname}
            renderItem={({ item }) => (
              <BrowserItem
                {...item}
                changeDescription={() => changeDescription(item.description)}
              />
            )}
          />
        </>
      )}
    </>
  );
}

export default Browsers;

將資料邏輯切割成更小可以重複使用的 hooks,當這些資料存取寫法被共用時就達到

  • 加速開發: 因為重複使用
  • 更快速找到問題: 遇到問題可以減少閱讀和測試這些部分
import { useEffect, useState } from "react";

function useFetch(url) {
  const [loading, setLoading] = useState(false);
  const [data, setData] = useState(undefined);

  useEffect(() => {
    setLoading(true);
    fetch(url)
      .then((response) => response.json())
      .then(setData)
      .finally(() => setLoading(false))
      .catch((error) => Alert.alert("Fetch error", error));
  }, [url]);

  return {
    loading,
    data,
  };
}

function useBrowsers(url) {
  const { loading, data } = useFetch(url);
  const [selectedBrowser, setSelectedBrowser] = useState(undefined);

  return {
    loading,
    browsers: data?.Browsers,
    selectedBrowser,
    setSelectedBrowser,
  };
}

切割成更小可重用的元件且拿掉大部分的資料邏輯,僅留下 conditional render 的相關實作,元件行數下降找問題的速度肯定又更上層樓。

function UIFriendlyList(props) {
  if (props.loading) {
    return <LoadingIndicator />;
  }

  if (props?.data && props.data.length === 0) {
    return <Text>This list is empty (</Text>;
  }

  return <List {...props} />;
}

// BrowsersList.tsx
function BrowsersList(props) {
  const { loading, selectedBrowser, setSelectedBrowser, browsers } = props;
  return (
    <View style={styles.container}>
      <AddModal
        modalVisible={Boolean(selectedBrowser)}
        onClose={() => setSelectedBrowser(undefined)}
        description={selectedBrowser?.description}
      />
      <UIFriendlyList
        loading={loading}
        data={browsers}
        renderItem={({ item }) => (
          <BrowserItem
            key={item.fullname}
            browser={item}
            onPress={() => setSelectedBrowser(item)}
          />
        )}
      />
    </View>
  );
}

function Browsers() {
  return <BrowsersList {...useBrowsers("https://google.com/myData.json")} />;
}

喜歡這篇文章,請幫忙拍拍手喔 🤣


share