iT邦幫忙

2023 iThome 鐵人賽

DAY 28
0

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

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

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

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

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

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

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

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

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

Yes

工具的選擇

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

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

工作的選擇

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

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

環境的選擇

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

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

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

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

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

程式撰寫的選擇

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

  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")} />;
}

上一篇
能不能給我一首歌的時間,把 Spec 說完才說再見
下一篇
未來的那個你,該成為工程師嗎?
系列文
前端三分鐘 X 每天三分鐘的斷捨離,讓每一天都可以早點下班30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言