PHP/後端工程師學習與面試指南

前言

每位後端工程師的工作資歷、工作環境與學習經歷都不一樣,大家心中對於後端工程師應該具備什麼技能、學習路線規劃和程度上 Junior/Senior 的定義也不相同。

本文旨在分享筆者個人在這幾年在軟體開發一路上學習路線的經驗,與近幾年累計大約超過百人以上的面試經歷提供大家在學習路線和未來面試過程中有個參考依據。

文章標題命名為 PHP/後端工程師主要是個人工作經歷上習慣使用的主要語言為 PHP,但是本文的內容也能適用在其他後端語言(如:NodeJS, Golang, Python 等),應該說 PHP 和其他語言的工程師除了使用語言的表層差別外,本質上在 Web 中所面臨與要解決的後端問題其實相去不遠。

工程師職級定義

在不同行業或公司中對於工程師 Junior 與 Senior 定義的標準都不相同,為了對齊下面段落中對於職級標準,本文借用 Avance Venture Lab 所分享的工程師職級定義,由比較抽象的層次來定義不同職級的標準:

avancevl.github.io/_main/people/engineering_level.md at master · avancevl/avancevl.github.io
AVL | 一家開源的新創 | An Open Sourced Incubator. Contribute to avancevl/avancevl.github.io development by creating an account on GitHub.

初級工程師 I~III

  • 大學、碩士畢業生或不到2年的全職軟體開發工作經驗。
  • 有一些相關知識或經驗。
  • 需要其他團隊成員的幫助。
  • 可以獨立開發部分網頁功能,但不能完整開發整個應用程式。
  • 會被分配開發項目,跟資深工程師合作、學習,能對不熟悉的領域技術進行研究,一起完成被指派的項目。

資深工程師 I~III

  • 至少2年相關的全職軟體開發工作經驗。
  • 可獨立完成每個分配的技術工程。
  • 有能力執行全端產品的開發。
  • 協助初級工程師解決問題,幫忙首席工程師完成開發。
  • 可以選擇自己想要開發的項目與合作團隊人員。

首席工程師 I~III

  • 團隊中最專業的工程師。
  • 負責推動新科技、技術、設計與工具。
  • 可以比任何人更快更完善的執行全端整個產品的開發。
  • 有責任設計並執行開發規劃,分配團隊工作,保持開發進度。
  • 有義務幫忙團隊其他工程師完成開發項目並給予建議,決定公司科技使用的方向、策略、團隊架構。
在許多公司中可能不一定有首席工程師的角色,那你可以考慮將其對應的技能也一併納入到資深工程師中。

技術總監/技術長

這個職位比較特殊一些,該角色除了決定技術架構選型與發展方向外,常常也要負責與技術以外的工作(如:部門管理與扮演和不同部門間溝通的窗口)。

而許多工程師雖然具備高度的技術能力,但因為人格特質與溝通能力的關係不一定能適合這個角色,即便是兼具這些特質的工程師也有可能因為只想專注在軟體開發上而不選擇往這個職位發展。

通常在這個角色中能親手接觸到開發的機會或時間會相比起專職技術開發的工作來少得多。

所以技術總監/技術長這個角色並不一定代表是在該工程團隊中技術能力最強的存在,真實的技術能力和負責的工作可能根據不同公司與產業有很大的差異。而本文主要著重技術能力上的描述,因此該職位不在本章節中不以技術角度明確定義其能力與職責。

技術能力條件

相信部分人應該有曾經看過 roadmap.sh 幫大家整理出來後端工程師所對應的相關技能樹,我認為裡面的對應項目整理得相當不錯。基本上你可以從裡面的類別中觀察出目前自己還有哪些領域較不熟悉,可以作為個人作為技能樹發展的參考依據。

而這裡想特別強調雖然在上個段落中有依據工作時間來當作相關參考,但實際上以筆者在 PHP 的面試經驗來說,有多年工作經驗的工程師在技術能力上往往不一定會比較資深。

有時候在履歷描述中看到像:`PHP 開發經驗 10 年` 這種描述我都會抖抖的...

因此本段落會依據筆者心中根據不同職級階段列出在技術上所對應需要具備的技術能力。而這些條件中和 PHP 相關的技術要求會依據不同後端程式語言而有差異,讀者可以自行以此概念去延伸擴展。

本文為針對技術能力上做討論,相關軟性能力不在討論範圍內,對於資深工程師的軟性能力定義我認為鐵哥在多年前的一篇文章中說明的不錯,有興趣的人可以參考:https://jaceju.net/be-a-senior-engineer/

初級工程師

  • 具備 Linux 系統基礎操作知識
  • 具備基礎演算法和資料結構概念
  • 具備物件導向設計觀念,熟悉 Abstract 與 Interface 等概念
  • 程式具備基礎 SOLID 設計概念
上述四點在此階段雖然具備相關概念,但實際應用上還有少部分不熟悉處是可接受的
  • 能熟悉使用 PHP 語言來進行開發,大概了解相關語言特性與限制,並遵守 PSR-12 的程式碼風格規範
  • 能熟悉使用 Git 或其他版本控管工具與團隊進行協作開發
遵循 Git flow、GitHub Flow、Gitlab Flow 或其他團隊定義的流程
  • 具備現代框架設計與開發經驗,如:Symfony 或 Laravel,並熟悉該框架的使用方式。以 Laravel 為例應只少熟悉:Middleware、Auth、Request、Response、Eloquent ORM、Command、File、HTTP Client、Cache、Collection、Events、Queues 等官方文件中有明列的元件
單純使用框架外殼,但內部程式的組織與架構完全不遵守 Laravel 規範不算數
  • 能理解 N+1 Query 與解決方式
  • 能熟悉使用 composer 管理相依的套件,遵守 PSR-4 規範
  • 能為負責開發的組件撰寫對應的 Unit Test
  • 理解 HTTP 與 Websocket 通訊協議,並能正確使用各種 HTTP Method 與 Response Code
對於一個 HTTP Request 如何從瀏覽器端至 HTTP Server 中間的過程能大致描述
  • 能正確的設計 RESTful API,並知道 GraghQL 的優缺點
  • 掌握至少一種 RDBMS 或 NoSQL 資料庫,並能根據業務需求正確設計出對應的 schema 與對應的資料查詢語句
  • 以 RDBMS 為例,需掌握一階、二階正規化與反正規化相關基礎知識
  • 具備基礎的資安相關防禦概念,如:SQL Injection、XSS 等
  • 接觸至少一種過 CI/CD 相關工具,並能完成簡單的自動化相關設定
  • 能使用 Redis 或 Memcache 當作 Cache Server,以 Redis 為例需能知道 Redis 中不同資料結構適用的情境
  • 知道 Queue 的基礎使用方式與情境
  • 能基本理解 VM 與 Docker 等原理與使用方式
  • 能撰寫基本的 Bash 指令

資深工程師

  • 對於 Linux 作業系統有除了基礎指令操作上更深的認識,如:

   - 能區別 process、thread 與 coroutine 在作業系統上調度的不同

   - 知道 system call 與 user call 的區別

   - 當 process 發生占用 CPU 100% 時如何知道該 process 正在忙什麼?

   - 理解 File Descriptor 的原理與運作方式

   - 何謂 OOM 與如何避免該問題?

  • 對於演算法和資料結構概念有更深刻的理解,並能在開發時活用在系統設計中
  • 熟悉並掌握各種常見的 Design Pattern
  • 掌握除了 PHP 以外至少一個其他語言的使用與特性
  • 能理解 PHP 更進階的語言特性,如:Magic Functions、Reflections、Generator、FFI 等
  • 理解在 PHP 中如何實現 Concurrent Requests
  • 熟悉 PHP 在直譯式語言的執行流程,如:SAPI、Syntax Parsing、OP Code、OP Cache、Zend Engine 等
  • 能區分 Blocking I/O 與 Non-blocking I/O 的差別,與知道在 PHP 中如何實現 Non-blocking I/O
  • 針對執行緩慢的 PHP 程式知道該如何有效的追蹤並分析 bottleneck,並進一步提出對應的解決方案
  • 具備 Multi-process 開發能力與 Process Pool 等相關概念
  • 以 Laravel 框架為例,能更進階地理解框架核心,如:

   - 完整理解一個 request 在 Laravel 中完整的生命週期

   - 熟悉 IoC、Service Provider 等元件

   - 理解各個基本元件中底層的 code 大致是如何實作的

   - 能理解各種 Design Pattern 在 Laravel 中如何被使用

   - 以 ORM 為例,能區別 Active Record 與 Data Mapper 設計上的優缺點

  • 能熟悉並理解 OSI Model,並掌握每一層中大致的通訊協議內容
  • 對於 TCP/UDP 協議有更深入的認知,如:

   - TCP 協議中的三項交握與四項揮手流程

   - HTTP 連線是如何升級成 Websocket 連線的?

   - 能區別短連線與長連線的優缺點

   - 在長連線中 heartbeat 機制是為了解決什麼問題?由 Server Side 和 Client Side 主動發起 heartbeat 封包的優缺點為何?

   - 知道如何針對相關協議錄製封包進行問題排查

  • 能更深入理解 Cache 的使用與問題,如:

   - Cache Missing

   - 雪崩問題

   - Cache Warming

   - Multi-layer Cache

   - Cache Revoking

  • 能更深入理解 Queue 的使用與問題:如:

   - 以 Redis 作為 Queue 有什麼優缺點?

   - Queue 應該具備哪些基礎與進階特性?

   - 為何有的 Queue 能保證順序性與不重複發送,而有的不能?

   - 如何處理重複消費的 Payload?

  • 對於資料庫有更深入的理解,以 MySQL 為例,如:

   - ACID 與 CAP 的原則代表什麼意思?

   - 能對於有效能問題的 SQL 語句進行性能分析與優化

   - 理解 Index 的使用與原理(如:為何 hash table 的效率是 O(1) 卻不使用其當作 Index 的資料結構?使用 B+ tree 的優缺點為何?)

   - 熟悉 Transactions 的使用與不同的 Isolation Levels

   - 能排查 Deadlock 發生原因與優化方式

   - 熟悉 Optimistic Lock 與 Pessimistic Lock 的使用情境與優缺點

   - 理解在 MySQL 中使用 Partition 與 Sharding 的情境與優缺點

   - 理解 MySQL 與其他 Database 間部分特性間的差異與優缺點

   - 使用 Auto Increment ID 作為 Primary Key 有什麼優缺點?

  • 熟悉與理解 Race Condition 與對應的解決方式
  • 能理解 Lock 的使用並且能區分 Spin Lock 與 Mutex 的差別
  • 在 Redis 中如何實現分布式鎖?需要注意什麼事?
  • 在微服務架構中,如何替代傳統 RDBMS 中 Transaction 的機制?
  • 能理解常見不同的併發請求情境並調整程式與系統架構,使系統能提升更高的請求量

首席工程師

  • 對於 Linux 除了單純使用外,能更認識相關 Linux 系統中底層的設計原理,如:

   - 理解 Linux 核心模組運作原理

   - 理解 Process 的 Context Switch 與排程機制

   - 理解 Linux 中 I/O Models 的演化與底層實作(如:select, poll 與 epoll 之間的差異)

   - Shared Memory 的實作方式,如:POSIX Shared Memory、mmap

  • 對於複雜的商業邏輯與技術問題能夠選用最合適的資料結構與演算法來解決問題
  • 除了理解既有的 Design Pattern 外也能知道哪些屬於 Anti-Pattern 的設計與選用取捨
  • 對於 PHP 的底層有更深刻的認識,如:PHP 5.6 到 PHP 7 間效能的提升是因為底層做了哪些優化?、zvalue、Zend VM 中記憶體分配與回收策略、JIT 機制等
  • 以 Laravel 為例,除了深入理解開發框架底層實作外,更能知道其設計的優缺點,且能將框架流程與元件進行深入調整與整合:

   - 依賴框架但也能脫離框架,假如今天因為某些限制不能使用 Laravel 框架,在其他架構中如何導入本來 Laravel 有的相關機制?或是今天採用其他框架該如何導入 Laravel 中好的設計機制與元件?

  • 對於除了 PHP 以外的語言(編譯式為佳)有熟悉的掌握,並能比較不同語言特性間的優缺點與底層實作
  • 針對 Redis 有更深入的底層知識掌握,如:

   - Redis 為何是 single-thread 卻那麼快?

   - Redis 中在 TTL 的實現中支援哪些策略?在 LRU 的實作上和傳統 LRU 算法有什麼差異?為什麼要這樣設計?

   - Redis 中如何實現 Transaction 機制?這些機制有哪些優缺點?
   - Redis 中針對 Storage Persistency 有提供哪些機制?優缺點分別為何?

   - 使用 Redis 作為 Queue 在不考慮 Storage Persistency 的前提下該如何確保 Queue 中資料的一致性?Job 執行失敗如何 Push 回 Queue 中?

Redis 其實不適合當作專業的  Queue,但很多後端框架中都會基於 Redis 實作 Queue,這裡探討以 Reids 當作 Queue 時的底層實作優缺點

   - Redis 有哪些指令操作是 Blocking?為何這些指令會被設計成 Blocking?

  • 針對資料庫有更深入得底層知識掌握,以 MySQL 為例,如:

   - 理解 Change Buffer 和 Redo Log 的相關原理

   - MySQL 什麼情況會選錯 Index?該如何解決問題?

   - 在 Order By 中 MySQL 有哪幾種演算法?優缺點分別為何?

   - 什麼是 Phantom Read?該如何解決此問題?

  • 能夠在面對複雜與大型業務需求時,隨時調整與設計出符合商業情境的系統架構
  • 能夠除了在既有的產品服務與使用工具外,研究出合適未來團隊使用的新技術或系統架構

總結

上面所列出的各職級預期的技術能力為筆者過往在團隊經驗中遇到的技術情境做大概的舉例,沒列出來的並不代表在該職級中不需要具備的技術能力,實際上在不同產品的業務情境中可能會包含上面沒列出來的技術領域。

但概念上的劃分方式可以簡單理解為:

  • 初級工程師:具有基礎的開發與規劃能力,但無法清楚理解技術較底層的細節,能針對大部分業務單位提出的需求進行交付。所實作的程式以可以執行就好、功能正確為原則,在可擴充性與維護性上可能無法兼顧,也無法解決較難的技術問題。
  • 資深工程師:在程式語言與工具上除了表面的使用外能更進一步理解底層的部分原理,對於單個問題可以想出多種解決方案並且分析後選出最合適的,且能排查大部分遇到的技術性問題。
  • 首席工程師:對於技術的各種層面在本質上都有更深一步的理解,並利用這些底層知識來解決複雜且困難的技術問題。對於每種解決方案都能清楚知道對應的優缺點,並同時研究除了現在可行方案外更進一步的可能性,領導團隊在技術上有所學習與成長。

而在台灣許多產業與公司的實際情境可能需要的技術角色大多是初級工程師~資深工程師I的角色,主要原因是產品性質的關係,如:

  • 對於多數接案或非技術導向的公司來說,面臨到的開發工作多數是簡單的 CRUD 和報表需求開發
  • 在低請求量的情境中大多時候你不需要具備太深的優化技能也可以讓你的產品穩定運作
  • 很多新創公司為了求快速市場驗證,初期對於技術不會有太高的要求,在使用者有明顯成長前也不會遇到太多複雜的技術問題

所以當你發現上面所列技能在目前公司中接觸不到,但你也被定位在資深工程師角色時,若你想繼續發展更進階的技術能力,除了自行利用時間研究外可以考慮往有更高技術需求的公司發展。

面試準備建議

這裡針對在面試準備前平時可以嘗試著手的項目,讓技術面試官在浩大的人選中能初步對於你的技術能力有較深刻的印象:

  • 保持撰寫技術部落格的習慣,即便是技術流水帳也沒關係
這個不容易,像筆者自己就很懶惰,無法長期維護技術部落格
  • 在 GitHub 上維護開源專案,或是偶爾針對有發現 bug 的專案提交 PR
  • 多參與技術社群活動,增加平時自己技術能力尚的不足
  • 在履歷中強調技術經歷(實際上運用什麼技術解決什麼複雜問題),而不是只列出公司負責業務的流水帳
  • 部分公司在技術面試前會有 Coding Test,可以平常沒事就到 Leet Code 刷刷題
也有許多公司是會有事前實作的作業,團隊人數規模越大的公司可能會較傾向使用 Coding Test 來篩選第一階段的人選
  • 除了快速 xxx 天上手或是 xxx 入門課程這種基礎入門資源外,平時可以嘗試多研究更底層的技術原理。技術能力除了橫向擴充外,縱向的能力培養也相當重要
越進階、底層的技術能力資源相較入門的資源量較少,除了繁體中文的文章閱讀外建議可以嘗試簡中或英文的技術文章。以筆者近年來的觀察在技術書籍上簡體中文的資源往往來的比繁體中文豐富許多。
  • 初階的技術面試會著重在如何使用工具(如:PHP 語法、 SQL 語法、框架如何使用、有沒有使用過 xxx?),而越進階的技術面試則會更看重技術本質上的理解與複雜架構選型優缺點分析
  • 技術面試沒有捷徑,需要靠平時一點點慢慢累積,過度的技術包裝在面對有技術能力的面試官時很容易被識破

最後祝大家在未來技術學習與面試準備中都能夠得心應手!