騰小云導讀
本文作者在騰訊多年,主要從事的是騰訊雲CDN、EdgeOne產品的後臺研發工作。作者在雲計算領域遇到了不少代碼設計和程序設計的問題,他對於如何把項目中的代碼標準化、提高開發維護的效率,做了總結梳理。本篇爲各位分享作者總結的代碼設計、架構設計原則和工作思維。歡迎閱讀~
目錄
1 萬物皆可抽象
1.1 遵循DRY原則
1.2 SOLID 原則
1.3 應用設計模式
2 高可用設計
2.1 設計高可用的系統架構
2.2 日誌和監控,包含服務級和業務級
2.3 做好容災設計 2.4 系統穩定是一名後臺開發的生命線
3 想盡一切辦法“偷懶”
3.1 優化重複流程、一切自動化
3.2 使用模板和標準化流程
3.3 應用項目管理工具和流程
4 從價值出發,專注於最重要的事
5 溝通與協作也是身爲開發的必備技能
6 凡事有交代,件件有着落,事事有迴音
7 保持開放和學習的心態
8 職業發展
9 橫向發展
01、萬物皆可抽象
1.1 遵循 DRY 原則
DRY 原則(Don’t Repeat Yourself),中文直譯爲:不要重複自己。將它應用在編程中,可以理解爲:不要寫重複的代碼。
或許有些開發者會想當然地認爲,只要代碼有重複就是違反 DRY 原則了。真的是這樣嗎?答案是否定的。實際上,重複的代碼不一定違反 DRY 原則,而有些看似不重複的代碼也有可能違反 DRY 原則。
DRY 原則的典型代碼重複情況有三種:實現邏輯重複、功能語義重複和代碼執行重複。下面我們簡單分享。
1.1.1 實現邏輯重複
以下是一個實現邏輯重複的例子:
class Calculator:
def add(self, a, b):
return a + b
def subtract(self, a, b):
return a - b
def multiply(self, a, b):
return a * b
def divide(self, a, b):
if b == 0:
# ...throw DivideByZeroException...
pass
return a / b
class AdvancedCalculator(Calculator):
def power(self, a, b):
result = 1
for i in range(b):
result *= a
return result
def square_root(self, a):
if a < 0:
# ...throw InvalidInputException...
pass
return math.sqrt(a)
在這個例子中,AdvancedCalculator 繼承了 Calculator,但是在 AdvancedCalculator中實現了 power 和 square_root 方法,這兩個方法的實現與 Calculator 中的 multiply 和 divide 方法有很多重複的邏輯。例如,power 方法中的循環可以使用 multiply 方法來實現,square_root 方法中的判斷可以使用 divide 方法來實現。這種重複的邏輯會導致代碼冗長、難以維護,並且容易出現錯誤。
1.1.2 功能語義重複
功能語義重複是指代碼中存在相同的功能語義,但是代碼的實現不同。例如,以下代碼中的兩個函數都實現了計算一個數的平方,但是它們的實現方式不同:
def square1(x):
return x ** 2
def square2(x):
return pow(x, 2)
可以看到,這段代碼中存在相同的功能語義,即計算一個數的平方,但是代碼的實現不同。如果我們遵循DRY原則,可以將計算一個數的平方抽象成一個函數,然後在需要計算平方的地方調用該函數,避免了功能語義的重複。
def square(x):
return x ** 2
1.1.3 功能語義重複
UserService中 login()用來校驗用戶是否登陸成功,如果失敗就返回異常,否則返回用戶信息。
class UserRepo:
def __init__(self, db):
self.db = db
def check_if_user_existed(self, email, password):
if not EmailValidation.validate(email):
# ...throw InvalidEmailException...
pass
if not PasswordValidation.validate(password):
# ...throw InvalidPasswordException...
pass
# ...query db to check if email&password exists...
def get_user_by_email(self, email):
if not EmailValidation.validate(email):
# ...throw InvalidEmailException...
pass
# ...query db to get user by email...
class UserService:
def __init__(self, user_repo):
self.user_repo = user_repo
def login(self, email, password):
existed = self.user_repo.check_if_user_existed(email, password)
if not existed:
# ...throw AuthenticationFailureException...
pass
user = self.user_repo.get_user_by_email(email)
return user
從上面代碼可以發現,login() 調用 UserRepo 的 check_if_user_existed() 和 get_user_by_email(),這兩個函數都校驗了一次 email 。我們可以將校驗代碼放到 login() 即可。
另外還有一個隱藏的執行重複問題,login()並不需要調用check_if_user_existed(),只需要調用一次 get_user_by_email() ,從數據庫中獲取用戶的 email、password 等信息跟用戶輸入的 email、password 信息做對比,依次判斷是否登陸成功。
實際上,這樣的優化是很有必要的,因爲兩個函數都查詢了數據庫,數據庫 I/O 操作是比較耗時的,在寫代碼的時候應當減少這類 I/O 操作。
按照剛剛的思路把代碼重構一下:
class UserService:
def __init__(self, user_repo):
self.user_repo = user_repo
def login(self, email, password):
if not EmailValidation.validate(email):
# ...throw InvalidEmailException...
pass
if not PasswordValidation.validate(password):
# ...throw InvalidPasswordException...
pass
user = self.user_repo.get_user_by_email(email)
if user is None or password != user.password:
# ...throw AuthenticationFailureException...
pass
return user
class UserRepo:
def __init__(self, db):
self.db = db
def check_if_user_existed(self, email, password):
# ...query db to check if email&password exists...
def get_user_by_email(self, email):
# ...query db to get user by email...
這樣做以後,整個代碼結構變得更清晰了,類的職責也更加統一。
身爲一名合格的程序員,要時刻謹記不幹“重複”的事情,能抽象的都做抽象,對後續系統的維護有很大的幫助。
1.2 SOLID 原則
在程序設計領域, SOLID(單一功能、開閉原則、里氏替換、接口隔離以及依賴反轉)是由羅伯特·C·馬丁在21世紀早期引入,指代了面向對象編程和麪向對象設計的五個基本原則。當這些原則被一起應用時,它們使得一個程序員開發一個容易進行軟件維護和擴展的系統變得更加可能。
1.2.1 單一職責原則(Single Responsibility Principle,SRP)
一個類應該只有一個職責。通過將職責抽象成不同的類或方法,我們可以更好地組織和管理代碼。
所謂職責是指類變化的原因。如果一個類有多於一個的動機被改變,那麼這個類就具有多於一個的職責。而單一職責原則就是指一個類或者模塊應該有且只有一個改變的原因。
很多人認爲這個原則是針對一個類進行描述,在我看來這裏使用模塊這個更加抽象的名詞更爲合適。在設計中,小到一個方法,大到一個模塊我們都應該儘量去遵循單一職責原則。
相信在我們日常開發中,每個人都遇到過改了一個問題經常會引起另一個問題。其原因大多都是因爲在設計上違背了單一職責原則。
如果一個類承擔的職責過多,就等於把這些職責耦合在一起了。一個職責的變化可能會削弱或者抑制這個類完成其他職責的能力。這種耦合會導致脆弱的設計,當發生變化時,設計會遭受到意想不到的破壞。而如果想要避免這種現象的發生,就要儘可能地遵守單一職責原則。
此原則的核心就是解耦合增強內聚性,是爲了實現代碼高內聚、低耦合,提高代碼的複用性、可讀性、可維護性。
還是上面的例子:
```
class UserManager:
def __init__(self, user_repo):
self.user_repo = user_repo
def create_user(self, name, email, password):
if not EmailValidation.validate(email):
# ...throw InvalidEmailException...
pass
if not PasswordValidation.validate(password):
# ...throw InvalidPasswordException...
pass
user = User(name, email, password)
self.user_repo.save_user(user)
def get_user_by_email(self, email):
if not EmailValidation.validate(email):
# ...throw InvalidEmailException...
pass
user = self.user_repo.get_user_by_email(email)
return user
class UserRepo:
def __init__(self, db):
self.db = db
def save_user(self, user):
# ...save user to db...
def get_user_by_email(self, email):
# ...query db to get user by email...```
還是類似的例子,UserManager 類負責用戶的創建和獲取,而 UserRepo 類負責用戶的存儲和查詢。這兩個類各自承擔了不同的職責,符合單一職責原則。如果將這兩個職責合併到一個類中,就會導致這個類的職責過於複雜,難以維護。
1.2.2 開閉原則(Open-Closed Principle,OCP)
當我們在軟件開發中添加新功能或修改現有功能時,我們希望這些更改不會破壞現有的代碼。這就是開閉原則(Open-Closed Principle,OCP)的核心思想:軟件實體(類、模塊、函數等)應該對擴展開放,對修改關閉。
換句話說,我們應該通過添加新的代碼來擴展軟件的功能,而不是修改現有的代碼。這樣做的好處是,我們可以保證現有的代碼仍然能夠正常工作,同時也可以避免引入新的錯誤。
以下是一個符合開閉原則的例子:
class Shape:
def area(self):
pass
class Rectangle(Shape):
def __init__(self, width, height):
self.width = width
self.height = height
def area(self):
return self.width * self.height
class Circle(Shape):
def __init__(self, radius):
self.radius = radius
def area(self):
return math.pi * self.radius ** 2
在這個例子中,Shape 類定義了一個抽象方法 area,表示計算形狀的面積。Rectangle 和 Circle 類繼承了 Shape 類,並實現了 area 方法來計算矩形和圓形的面積。如果我們需要添加一個新的形狀,例如三角形,我們只需要創建一個新的類並實現 area 方法即可,而不需要修改現有的代碼。
這個例子中的代碼符合開閉原則,因爲它對擴展開放(我們可以添加新的形狀),對修改關閉(我們不需要修改現有的代碼)。
遵循開閉原則的好處是,我們可以編寫具有可擴展性和可維護性的代碼,從而提高軟件開發的效率和質量。如果我們的代碼不具備這些特性,那麼在軟件開發過程中就會遇到很多問題,例如:
當我們需要添加新的功能時,我們可能需要修改現有的代碼,這可能會破壞現有的功能或引入新的錯誤。 當我們需要修改現有的代碼時,我們可能會影響到其他部分的代碼,這可能會導致其他功能出現問題。 當我們需要維護代碼時,我們可能會發現代碼難以理解或難以修改,這可能會導致維護成本增加。 |
---|
遵循開閉原則是非常重要的,它可以幫助我們編寫具有可擴展性和可維護性的代碼,從而提高軟件開發的效率和質量。
1.2.3 里氏替換原則(Liskov Substitution Principle,LSP)
里氏替換原則(Liskov Substitution Principle,LSP)是 SOLID 原則中的第三個原則,它強調了子類應該能夠替換其父類並且不會影響程序的正確性。
里氏替換原則強調的是設計和實現要依賴於抽象而非具體;子類只能去擴展基類,而不是隱藏或者覆蓋基類,它包含以下4層含義
子類可以實現父類的抽象方法,但不能覆蓋父類的非抽象方法,父類中凡是已經實現好的方法(相對於抽象方法而言),實際上是在設定一系列的規範和契約,雖然它不強制要求所有的子類必須遵從這些契約,但是如果子類對這些非抽象方法任意修改,就會對整個繼承體系造成破壞。
以下是一個符合里氏替換原則的例子:
class Rectangle:
def __init__(self, width, height):
self.width = width
self.height = height
def get_width(self):
return self.width
def set_width(self, width):
self.width = width
def get_height(self):
return self.height
def set_height(self, height):
self.height = height
def area(self):
return self.width * self.height
class Square(Rectangle):
def __init__(self, size):
self.width = size
self.height = size
def set_width(self, width):
self.width = width
def set_height(self, height):
self.width = height
在這個例子中,Rectangle類表示一個矩形,它有一個 width 和一個 height 屬性,以及一個計算面積的方法 area。Square 類繼承了 Rectangle 類,並重寫了 set_width 和 set_height 方法,以確保正方形的寬和高始終相等。
這個例子中的代碼符合里氏替換原則,因爲我們可以用 Square 類的實例替換 Rectangle 類的實例,並且程序的行爲不會受到影響。這是因爲 Square 類繼承了 Rectangle 類的行爲,並且沒有改變它。
1.2.4 接口隔離原則(Interface Segregation Principle,ISP)
接口隔離原則也叫Interface Segregation Principle、ISP,要求程序員儘量將臃腫龐大的接口拆分成更小的和更具體的接口,讓接口中只包含客戶感興趣的方法。2002 年羅伯特·C·馬丁給“接口隔離原則”的定義是:客戶端不應該被迫依賴於它不使用的方法(Clients should not be forced to depend on methods they do not use)。
該原則還有另外一個定義:一個類對另一個類的依賴應該建立在最小的接口上(The dependency of one class to another one should depend on the smallest possible interface)。
以上兩個定義的含義是:要爲各個類建立它們需要的專用接口,而不要試圖去建立一個很龐大的接口供所有依賴它的類去調用。
感覺可能與單一職責原則很像,接口隔離原則和單一職責都是爲了提高類的內聚性、降低它們之間的耦合性,體現了封裝的思想,但兩者是不同的:
單一職責原則注重的是職責,而接口隔離原則注重的是對接口依賴的隔離。 單一職責原則主要是約束類,它針對的是程序中的實現和細節;接口隔離原則主要約束接口,主要針對抽象和程序整體框架的構建。 |
---|
簡單來說接口隔離原則與單一職責的定義的規則是不相同的,單一職責要求的是類和接口職責單一,注重的是職責,沒有要求接口的方法減少,例如一個職責可能包含 10個方法,這 10個方法都放在一個接口中,並且提供給多個模塊訪問,各個模塊按照規定的權限來訪問,在系統外通過文檔約束不使用的方法不要訪問,按照單一職責原則是允許的,按照接口隔離原則是不允許的。
1.2.5 依賴倒置原則(Dependency Inversion Principle,DIP)
依賴倒置原則(Dependency Inversion Principle,簡稱DIP)是面向對象設計原則之一,它強調高層模塊不應該依賴於低層模塊,而是應該依賴於抽象。換句話說,依賴關係應該建立在抽象層次上,而不是具體實現層次上。這樣可以降低模塊間的耦合度,提高系統的可擴展性和可維護性。
依賴倒置原則主要包含以下兩個方面:
高層模塊不應該依賴於低層模塊,兩者都應該依賴於抽象。 抽象不應該依賴於具體實現,具體實現應該依賴於抽象。 |
---|
爲了更好地理解依賴倒置原則,我們可以通過以下幾個步驟來應用它:
識別模塊間的依賴關係:在設計系統時,我們需要識別出各個模塊之間的依賴關係,以便確定哪些模塊需要遵循依賴倒置原則。
定義抽象接口:爲了實現依賴倒置原則,我們需要定義一個抽象接口,讓高層模塊和低層模塊都依賴於這個接口。這樣,高層模塊就不再直接依賴於低層模塊的具體實現,而是依賴於抽象接口。 實現抽象接口:低層模塊需要實現抽象接口,這樣高層模塊就可以通過抽象接口與低層模塊進行交互。這種方式可以確保高層模塊與低層模塊之間的解耦,提高系統的可擴展性和可維護性。 |
---|
依賴倒置原則是一種有效的設計原則,可以幫助我們構建更加靈活、可擴展和可維護的系統。通過將依賴關係建立在抽象層次上,我們可以降低模塊間的耦合度,從而提高系統的整體質量。
1.2.6 小結
原則 | 含義 |
---|---|
單一職責原則 (Single Responsibility Principle,SRP) | 一個類應該只有一個職責。通過將職責抽象成不同的類或方法,我們可以更好地組織和管理代碼。 |
開閉原則 (Open-Closed Principle,OCP) | 一個類應該對擴展開放,對修改關閉。通過使用抽象類或接口來定義通用的方法或屬性,我們可以更好地實現代碼的擴展性。 |
里氏替換原則 (Liskov Substitution Principle,LSP) | 子類應該能夠替換父類。通過使用抽象類或接口來定義通用的方法或屬性,我們可以更好地實現代碼的可擴展性和可維護性。 |
接口隔離原則 (Interface Segregation Principle,ISP) | 一個類不應該依賴於它不需要的接口。通過將接口抽象成不同的類或方法,我們可以更好地組織和管理代碼。 |
依賴倒置原則 (Dependency Inversion Principle,DIP) | 高層模塊不應該依賴於低層模塊,它們應該依賴於抽象。通過使用抽象類或接口來定義通用的方法或屬性,我們可以更好地實現代碼的可擴展性和可維護性。 |
1.3 應用設計模式
設計模式是一種常見的代碼設計思想,它可以幫助我們解決常見的代碼設計問題。通過使用抽象思維,我們可以更好地理解和應用設計模式,以便更好地組織和管理代碼。
抽象思維在代碼設計中非常重要。通過使用不同的代碼設計思想和準則,我們可以將具體的事物抽象成一般性的概念或模型,以便更好地組織和管理代碼。一堆抽象度很低的代碼,很容易就會成爲我們項目當中人人唾棄的對象(簡稱代碼“屎山”)。
這裏總結了23種常見的設計模式,在做系統設計時,可以根據需求,使用對應的設計模式:
類別 | 設計模式 | 簡要介紹 |
---|---|---|
創建型模式 | 單例模式 | 確保一個類只有一個實例,並提供一個全局訪問點。 |
工廠方法模式 | 定義一個創建對象的接口,讓子類決定實例化哪一個類。 | |
抽象工廠模式 | 提供一個接口,用於創建相關或依賴對象的家族,而不需要明確指定具體類。 | |
建造者模式 | 將一個複雜對象的構建與其表示分離,使得同樣的構建過程可以創建不同的表示。 | |
原型模式 | 通過複製現有的實例來創建新的實例。 | |
結構型模式 | 適配器模式 | 將一個類的接口轉換成客戶期望的另一個接口。 |
橋接模式 | 將抽象部分與實現部分分離,使它們可以獨立地變化。 | |
組合模式 | 將對象組合成樹形結構以表示“部分-整體”的層次結構。 | |
裝飾器模式 | 動態地給一個對象添加一些額外的職責。 | |
外觀模式 | 爲子系統中的一組接口提供一個統一的接口。 | |
享元模式 | 使用共享技術有效地支持大量細粒度的對象。 | |
代理模式 | 爲其他對象提供一個代理以控制對這個對象的訪問。 | |
行爲型模式 | 責任鏈模式 | 爲請求創建一個接收者對象的鏈。 |
命令模式 | 將一個請求封裝爲一個對象,從而使您可以用不同的請求對客戶進行參數化。 | |
解釋器模式 | 給定一個語言,定義它的文法的一種表示,並定義一個解釋器。 | |
迭代器模式 | 提供一種方法順序訪問一個聚合對象中各個元素,而又不暴露該對象的內部表示。 | |
中介者模式 | 用一箇中介對象來封裝一系列的對象交互。 | |
備忘錄模式 | 在不破壞封裝的前提下,捕獲一個對象的內部狀態,並在該對象之外保存這個狀態。 | |
觀察者模式 | 當一個對象的狀態發生改變時,其相關依賴對象會被自動更新。 | |
狀態模式 | 允許一個對象在其內部狀態改變時改變它的行爲。 | |
策略模式 | 定義一系列算法,把它們一個個封裝起來,並且使它們可互相替換。 | |
模板方法模式 | 定義一個操作中的算法的骨架,而將一些步驟延遲到子類中。 | |
訪問者模式 | 在不改變數據結構的前提下,定義作用於某種數據結構中的各元素的新操作。 |
02、高可用設計
2.1 設計高可用的系統架構
高可用的系統架構是實現系統高可用性的基礎。因此,我們應該設計高可用的系統架構,包括負載均衡、集羣、分佈式、異地多活等特點。例如,我們可以使用騰訊雲CLB來實現負載均衡,使用Kubernetes來實現容器集羣,使用分佈式數據庫來實現數據的高可用性,使用異地多活來實現系統的容災和備份。
在系統設計之初,一定要避免出現服務單點問題!
2.2 日誌和監控,包含服務級和業務級
監控是保證系統高可用性的重要手段。我們應該做好各路的監控,包括系統的性能監控、服務的健康監控、網絡的流量監控、日誌的收集和分析等。通過監控,我們可以及時發現系統的異常和故障,快速定位和解決問題,保證系統的穩定性和可用性。
在做日誌和監控設計時,應該考慮以下因素:
可讀性:日誌和監控應該易於閱讀和理解,以便快速定位和解決問題。 可靠性:日誌和監控應該具有高可靠性,以確保監控結果的準確性。 可擴展性:日誌和監控應該能夠輕鬆地擴展,以滿足未來的需求。 實時性:日誌和監控應該具有實時性,以便快速發現和解決問題。 |
---|
2.3 做好容災設計
容災設計對於一個系統的穩定性來說至關重要。從系統和架構設計的角度,做好服務容災需要綜合考慮多個方面,包括分佈式架構、多數據中心冗餘、數據備份與恢復、容災預案、監控告警、容量規劃等。通過這些措施,可以提高系統的可用性和容錯能力,確保在發生災難時能夠快速恢復服務。
對於部分系統,在發生故障時,選擇性地進行服務降級,也是一種有效的手段:犧牲掉不重要的服務,保證核心服務的正常運行。
下面有一些容災設計的準則,供大家參考:
分佈式架構:採用分佈式架構可以提高系統的可用性和容錯能力。通過將服務拆分成多個微服務,並部署在不同的服務器上,可以降低單點故障的風險。同時,分佈式架構還可以提高系統的伸縮性,以應對不同的業務需求。 多數據中心和地域冗餘:爲了應對自然災害或其他不可抗力因素,可以將服務部署在多個數據中心和地域。這樣,即使某個數據中心出現故障,其他數據中心仍可以繼續提供服務。同時,通過使用負載均衡和智能路由技術,可以確保用戶請求被分配到最近的可用數據中心。 數據備份和恢復:定期對關鍵數據進行備份,並將備份數據存儲在安全的地方,以防數據丟失。同時,需要制定數據恢復計劃,並定期進行恢復演練,以確保在發生災難時能夠快速恢復數據。 容災預案:制定詳細的容災預案,包括災難發生時的應急響應流程、責任人、通訊方式等。並定期組織演練,以提高團隊在應對災難時的協同能力。 監控和告警:建立全面的監控體系,對系統的各個環節進行實時監控,包括服務器、網絡、數據庫、應用等。一旦發現異常,立即觸發告警,通知相關人員進行處理。 容量規劃:根據業務發展趨勢和歷史數據,進行容量規劃,確保系統具備足夠的資源來應對未來的業務增長。同時,需要定期進行容量評估,以便及時調整資源分配。 |
---|
2.4 系統穩定是一名後臺開發的生命線
穩定是後臺開發的生命線。我們應該注重系統的穩定性和可靠性,從設計、開發、測試、部署、運維等各個環節,都要注重穩定性和可靠性驗證。我們應該遵循“穩定優先”的原則,保證系統的穩定性和可用性,爲用戶提供更好的服務和體驗。
此外,騰訊雲開發者公衆號也分享過不少關於高可用系統的建設思路和經驗如工作十年,在騰訊沉澱的高可用系統架構設計經驗、騰訊專家10年沉澱:後海量時代的架構設計。
03、想盡一切辦法“偷懶”
這裏說的“偷懶”並不是讓大家真的去偷懶,而是一種思考問題的方式。
打個比方,產品經理經常會要求我們導出近期一段時間內,用戶的活躍數據、用戶的付費意願、域名的帶寬增長情況、某個新上功能的使用情況等,每次的要求都有所不同,非常耗費和佔用研發的時間。後面我們想了個辦法,我們將這些運營基礎數據(用戶緯度、域名緯度、功能緯度),通通上報到公司的BI系統(類似於一個運營報表生成平臺),讓產品自行決策需要哪些數據、怎麼使用這些數據,這樣研發既不需要再給產品經理導出數據,又可以讓產品經理自己隨意“折騰”,雙方的效率都得到了很大的提升。
下面這些方法論可以供給大家參考。
3.1 優化重複流程、一切自動化
在工作中,我們經常需要處理重複的流程,例如手動導出數據、手動處理數據等等。這些重複的流程不僅浪費時間和精力,還容易引入錯誤和漏洞。建議定期覆盤近期一直在重複做的事情,主動優化掉這些重複的工作內容。
3.2 使用模板和標準化流程
模板和標準化流程是另一種避免重複流程的方法。例如,我們可以使用標準化流程來處理數據,使用模板來生成重複的文檔或報告等等。舉個例子,我們需要每週向客戶發送一份報告,將每週的運營數據和相關情況同步給客戶。其實我們完全可以創建了一個標準化的報告模板,並使用自動化腳本來生成報告,這樣大大減少了手動編寫報告的工作量,提高了工作效率。
3.3 應用項目管理工具和流程
項目管理工具和流程是一種常見的管理方法,它們可以幫助我們優化重複流程,提高工作效率。例如,我們可以使用項目管理工具來跟蹤任務和進度,使用流程圖來優化流程等等。
04、從價值出發,專注於最重要的事
我們應該始終堅持從用戶價值和需求價值出發,關注用戶的需求和痛點,爲用戶提供有價值的產品和服務。同時,我們也應該多問一些爲什麼,思考這件事是否是當下最重要的事情,這個事情不做或怎麼樣,當前我最重要的是做什麼。只有選對了方法和方向,才能做出有意義的事情,爲用戶和整個產品創造價值。
在我過去的經驗裏面,很多時候前方接觸客戶的同學經常會叫着說“XX客戶要求實現一個怎麼怎麼樣的需求,我們趕緊搞起來”,但往往這個時候,我會反問一句:“這個事情不做會怎麼樣?做了又能帶來什麼收益?我們是不是應該從整個系統設計的角度,提前就滿足好這類客戶的需求,而不是臨時抱佛腳?”。其實我們會發現,往往就因爲這麼多問一句,會發現其實這個需求並沒有那麼重要,更重要的是我們需要提煉出更多通用化系統化的能力,提前滿足到這類用戶/客戶的場景,對於我們整個系統和產品的長遠發展來說顯得尤爲重要。
如果我們總是在幹一些快速和短期的事情,就需要思考我們是否真的在朝着我們的目標在進發了。
05、溝通與協作也是身爲開發的必備技能
在軟件開發中,溝通能力是非常重要的技能。很多開發同學會直接把自己簡單純粹地定義爲“一個只負責寫代碼的人”,這其實是一個思想上的誤區。在開發過程中,程序員其實需要與團隊成員如產品經理,甚至用戶或客戶進行大量的溝通,以便更好地理解需求、協同工作、解決問題等。
- 更好地理解需求
程序員需要與產品經理,甚至用戶進行溝通,以便更好地理解需求。只有理解了用戶和客戶的需求,才能開發出符合需求的軟件產品。如果程序員沒有良好的溝通能力,就很難理解用戶和客戶的需求,從而導致開發出的軟件產品與用戶和客戶的需求不符。
- 協同工作
稍微大點的項目開發,往往是一場“團戰”。程序員需要與其他同事進行溝通,以便更好地協同。只有通過良好的溝通,團隊成員才能理解彼此的需求和想法,協同完成項目的開發和交付。如果程序員沒有良好的溝通能力,就很難與團隊成員進行有效的溝通和協作,從而導致項目的延誤和質量問題。
- 解決問題
在軟件開發中,問題是難以避免的。程序員需要與團隊成員、用戶、客戶等進行溝通,以便更好地解決問題。只有通過良好的溝通,才能及時發現和解決問題,保證項目的順利進行。如果程序員沒有良好的溝通能力,就很難與團隊成員、用戶、客戶等進行有效的溝通和問題解決,從而導致問題的滋生和擴大。
- 推動和解決
在大公司裏面,推動溝通能力也是一種掌控資源的能力,我們往往會發現,溝通不好的人推動事情總是很慢,總是獲得不到他想要的資源,導致事情一直難以獲得落地。
打個比方,TEAM A 的小 X 是一名程序員,雖然技術能力很強但不善言辭,他在項目上有一個獲取 IP 地理位置的需求,剛好另一個團隊 TEAM B 有這塊的一些積累,小 X 希望能調用他們的接口,獲取到 IP 的一些地理位置信息。但奈何 TEAM B 的同學對這個事情提不起興趣,覺得這不是他們的義務,也沒什麼收益,所以不管小 X 怎麼努力,TEAM B 的人就是不予理睬,不配合。這時候小 X 的一位同事小 Y 發現了這個問題,並協助他一塊解決,小 Y 是怎麼做的呢,首先小 Y 拉上了 TEAM A 和 TEAM B 團隊的領導,並說明了這個事情的重要性、對公司的意義、對雙方團隊的收益,很快,TEAM B 的領導馬上就安排了一名團隊核心骨幹同學參與支持,並解決了問題,小 X 對小 Y 佩服得五體投地。
所以,溝通能力對於程序員來說也非常重要,千萬別小看溝通這項技能,畢竟身爲程序員,除了寫代碼以外,還有很長的時間都在和人在打交道。
06、凡事有交代,件件有着落,事事有迴音
“凡事有交代,件件有着落,事事有迴音”,其實翻譯過來就是“做事靠譜”,負責的事情一定會認真負責到底,及時反饋和響應。
成爲一位靠譜同事,你的行爲準則就是你個人的銘牌,同事會十分願意與你合作。職場本質上也是一個互相支持的過程,你對別人工作的支持,在你有求時別人也會積極支持你。
成爲一位靠譜的員工,上司會非常喜歡這類員工,工作認真負責,交辦的事可控。你的行事風格,會讓上司看在眼裏,記在心裏,在上司打考覈的時候也會考慮到這一點。
遵循“凡事有交代,件件有着落,事事有迴音”原則,能夠更好地完成自己的工作任務,提大家整體的協作效率,在我看來也是非常重要的一項行爲準則。
07、保持開放和學習的心態
在當前技術更新迭代速度非常快的時代,程序員需要時常保持有一顆學習的心態。
- 技術更新迭代速度非常快
當前的技術更新迭代速度非常快,新技術和新工具層出不窮。如果程序員不保持開放和學習的心態,就很容易被淘汰,無法適應新的技術,從而影響長期的職業發展。
- 學習是程序員的必備技能
作爲程序員,學習是必備的技能。只有不斷學習新的技術和工具,才能保持自己的競爭力和創新能力。比如最近火爆全網的chatgpt,如果不及時跟進學習類似的AI工具,相信很快就會有不少的人被淘汰掉。
- 學習可以提高工作效率和質量
學習新的技術和工具可以提高程序員的工作效率和質量。新的技術和工具通常都有更高效的解決方案和更好的性能,可以幫助程序員更快地完成工作任務,提高工作效率和質量。 比如GO語言在1.18版本新推出的泛型支持,可以很好地解決GO中缺乏泛型,導致一個相同功能的函數,需要爲不同的變量類型重複寫很多遍函數的問題。
總體來說,程序員我認爲是一個需要保持終身學習的職業,知識永遠都在往前更新迭代,我們需要做的,就是保持一顆開放和學習的心態,對技術保持敏感和熱度。
08、職業發展
職場中難免遇到不好的團隊、不好的項目,這種時候該怎麼辦?從三個角度出發:
· 是否工作得開心。
· 在這裏工作是否能持續得到自我提升和自我滿足。
· 金錢方面是否能夠滿足。
如果三個裏面有兩個都是“NO”,那麼你可能需要考慮一下換份工作了,換一個更廣闊的平臺,找到一條合適自己的道路。每個人都是一個自由而獨立的個體,我們大可不必吊死在一棵樹上。
09、橫向發展
作爲一名開發,往往會將自己的視野僅僅聚焦在手頭在做的事情上面,忽略了自己身份是一個職業人的事實。因此,多做“橫向發展”非常重要:
- 增強職業競爭力
除了技術能力之外,軟性技能也非常重要。例如溝通能力、團隊合作能力、領導力等,都可以幫助我們更好地完成工作任務,提高工作效率和質量。如果程序員只關注技術能力,而忽略了軟性技能,就會在職業競爭中處於劣勢,畢竟我們的工作內容可不只有敲代碼這件事。
- 拓寬職業發展道路
多元化發展可以拓寬職業發展道路。程序員可以通過學習其他領域的知識和技能,拓寬自己的職業發展道路。例如,學習產品設計、市場營銷等知識,可以幫助我們更好地理解客戶需求和市場趨勢,從而更好地完成工作任務,實現職業發展。
- 未來的選擇更多樣化
多做一些其他領域的知識儲備,可以在我們未來無法繼續勝任開發工作的時候,有更多的職業選擇。擁有技術背景的我們,轉到其他崗位上也會有如虎添翼的感覺。
以上就是本篇文章的全部內容啦,如果覺得內容有用,歡迎轉發收藏~
聊一聊令你受益的職場思維(點這裏進入開發者社區,右邊掃碼即可進入公衆號)。我們將選取1則最有創意的分享,送出騰訊雲開發者-馬克杯1個(見下圖)。5月31日中午12點開獎。