偷偷摘套内射激情视频,久久精品99国产国产精,中文字幕无线乱码人妻,中文在线中文a,性爽19p

受夠了“系統(tǒng)異常”!

系統(tǒng) 其他OS
技術(shù)負(fù)責(zé)人必須為所有成員提供系統(tǒng)培訓(xùn),并公開(kāi)建立團(tuán)隊(duì)代碼標(biāo)準(zhǔn)。他們應(yīng)堅(jiān)決拒絕不符合標(biāo)準(zhǔn)的 pull 請(qǐng)求,并與那些屢教不改的人進(jìn)行“黑房對(duì)話”。

作為用戶(hù),你是否有過(guò)這樣的經(jīng)歷:使用軟件時(shí)偶爾彈出一個(gè)消息,顯示“系統(tǒng)異常!”?

作為程序員,你是否有過(guò)這樣的經(jīng)歷:

運(yùn)維同事跑來(lái)求助:“用戶(hù)不能下單了!”

“顯示什么錯(cuò)誤?”

“系統(tǒng)異常!”

無(wú)論是作為用戶(hù)還是程序員,當(dāng)看到這四個(gè)字“系統(tǒng)異?!睍r(shí),我都感到不安。

它只告訴我系統(tǒng)有問(wèn)題,卻沒(méi)有提供任何有價(jià)值的信息。

這通常標(biāo)志著程序員另一個(gè)痛苦日子的開(kāi)始。

我們無(wú)法獲取任何有價(jià)值的信息,只能盲目地到處查找。

首先,我們檢查系統(tǒng)負(fù)載。嗯,沒(méi)問(wèn)題。

然后,我們查看錯(cuò)誤日志。成堆的日志滾動(dòng)不斷,但似乎沒(méi)有任何意義。

于是,我們不得不向運(yùn)維同事求助:“能否幫我們獲取用戶(hù)的電話號(hào)碼或賬號(hào)信息?另外,他們的手機(jī)型號(hào)和版本也會(huì)有幫助!如果可以,請(qǐng)錄制一個(gè)視頻!”

等待了好像一個(gè)世紀(jì),運(yùn)維同事終于收集到了這些信息。然后我們花費(fèi)數(shù)小時(shí)查看各種日志,仔細(xì)審查每一行代碼,最終找到了錯(cuò)誤所在。

為什么會(huì)有“系統(tǒng)異常”?

那些喜歡將所有外部錯(cuò)誤信息寫(xiě)成“系統(tǒng)異常”的人通常有以下幾種原因:

  1. 剛進(jìn)入行業(yè)的新手,還沒(méi)有經(jīng)歷過(guò)程序員的辛苦。
  2. 相信“敏感信息”,對(duì)他們來(lái)說(shuō),任何系統(tǒng)錯(cuò)誤信息都是敏感的,必須“包裝”起來(lái)。
  3. 公司所在的行業(yè)敏感,強(qiáng)制要求如此處理。

我見(jiàn)過(guò)一些系統(tǒng)是這樣處理的:

class BaseController {
    errorHandler(err) {
        this.response.sendJSON({code: 500, message: '系統(tǒng)異常'})
    }
}

這意味著這個(gè)系統(tǒng)的所有拋出的錯(cuò)誤都會(huì)轉(zhuǎn)換為“系統(tǒng)異常”!

而最糟糕的是,甚至沒(méi)有記錄任何日志!

為了方便后續(xù)開(kāi)發(fā)人員定位錯(cuò)誤,各種日志被添加到業(yè)務(wù)層代碼中,使得業(yè)務(wù)代碼不堪重負(fù)。

“系統(tǒng)異?!睈?ài)好者的改進(jìn)措施

上述極端代碼相對(duì)較少見(jiàn)。通常,我們會(huì)遇到這樣的情況:

class BaseController {
    errorHandler(err) {
        // 生成異常標(biāo)識(shí)符并記錄日志。
        let flag = random()
        log(err, flag)
        this.response.sendJSON({"code": 500, "message": `系統(tǒng)異常(${flag})`})
    }
}

在系統(tǒng)異常后添加一個(gè)標(biāo)識(shí)符。當(dāng)出現(xiàn)問(wèn)題時(shí),可以根據(jù)這個(gè)標(biāo)識(shí)符快速定位和排查日志。對(duì)于擁有完善日志系統(tǒng)的項(xiàng)目(如 ELK),大大提高了程序員的生存狀態(tài)。

但是,上述代碼有什么問(wèn)題?

假設(shè)某個(gè)支付邏輯有如下代碼:

if (balance < amount) {
    throw new NotEnoughException('卡余額不足。')
}

余額不足是一個(gè)非常常見(jiàn)的場(chǎng)景,但用戶(hù)看到的提示是:“系統(tǒng)異常(1877618)”。

此時(shí),我不知道用戶(hù)和程序員是否崩潰了,但至少你的老板是崩潰的。

“系統(tǒng)異?!钡慕K結(jié):錯(cuò)誤代碼的出現(xiàn)

“系統(tǒng)異?!币l(fā)的事情讓人憤怒。如今,已經(jīng)沒(méi)有多少信徒了。要么他們?cè)趬毫ο赂淖兞俗龇?,要么已?jīng)被主管完全開(kāi)除了。

現(xiàn)在,你更有可能遇到這樣的代碼:

配置文件:

// 全局:定義統(tǒng)一的錯(cuò)誤代碼和錯(cuò)誤消息。
const OK = 200
const SYS_ERR = 500
const NOT_FOUND = 404
const NOT_ENOUGH = 405

const map = {
    200: "OK",
    500: "系統(tǒng)錯(cuò)誤",
    404: "資源未找到",
    405: "余額不足"
}

// 錯(cuò)誤代碼轉(zhuǎn)文本
function error(code) {
    return map[code]
}

業(yè)務(wù)層代碼:

if (balance < amount) {
    // 此自定義異常類(lèi)只允許傳遞錯(cuò)誤代碼,并在內(nèi)部使用 error() 函數(shù)將其轉(zhuǎn)換為文本。
    throw new MyException(NOT_ENOUGH)
}

控制器:

class BaseController {
    errorHandler(err) {
        log(err)
        this.response.sendJSON({"code": err.code, "message": err.message})
        // 或者:this.response.sendJSON({"code": err.code, "message": error(err.code)})
    }
}

這種錯(cuò)誤處理原則是通過(guò)錯(cuò)誤代碼統(tǒng)一項(xiàng)目的代碼和消息。開(kāi)發(fā)人員不能在程序中定義自己的錯(cuò)誤描述。

我稱(chēng)這些程序員為“錯(cuò)誤代碼”信徒。

“錯(cuò)誤代碼”組的主要擔(dān)憂是,如果允許開(kāi)發(fā)人員在代碼中定義錯(cuò)誤描述,可能會(huì)導(dǎo)致“哈姆雷特”問(wèn)題,即每個(gè)人的描述可能不同,還可能導(dǎo)致敏感信息泄漏。

相比“系統(tǒng)異?!保板e(cuò)誤代碼”組取得了顯著進(jìn)步。大家終于知道系統(tǒng)中發(fā)生了什么錯(cuò)誤,老板們也不再擔(dān)心因?yàn)榭蛻?hù)卡余額不足導(dǎo)致的“系統(tǒng)異常”損害品牌形象。

當(dāng)用戶(hù)購(gòu)買(mǎi)500元商品時(shí),收到的提示是“余額不足”,而更好的提示應(yīng)該是“余額不足,目前可用余額:420.00”。

當(dāng)根據(jù) userId 找不到用戶(hù)信息時(shí),應(yīng)該顯示“用戶(hù)不存在”的提示,但開(kāi)發(fā)人員不應(yīng)僅因?yàn)椴幌攵x新代碼而直接使用 404(資源未找到)。

錯(cuò)誤代碼的問(wèn)題

錯(cuò)誤代碼的問(wèn)題在于它們的文本提示過(guò)于模糊,導(dǎo)致某些錯(cuò)誤場(chǎng)景中丟失了重要的有價(jià)值信息(這導(dǎo)致未解決問(wèn)題的排查困難),同時(shí)在其他場(chǎng)景中導(dǎo)致用戶(hù)體驗(yàn)不佳。

對(duì)于開(kāi)發(fā)人員來(lái)說(shuō),它帶來(lái)了兩種影響:一些開(kāi)發(fā)人員不愿定義大量新錯(cuò)誤代碼,所以他們湊合使用現(xiàn)有代碼,導(dǎo)致錯(cuò)誤提示不一致;其他開(kāi)發(fā)人員傾向于為幾乎每個(gè)異常定義大量錯(cuò)誤代碼(認(rèn)為每個(gè)異常的文本提示不同),最終導(dǎo)致錯(cuò)誤代碼的失控增長(zhǎng)。

改進(jìn)錯(cuò)誤代碼

改進(jìn)非常簡(jiǎn)單,只需允許異常類(lèi)傳入自定義描述。

// 添加可選參數(shù) "message",以允許自定義描述輸入。
class MyException {
    constructor(code, message = '') {
        this.code = code
        this.message = message
    }
}

期望程序有如下調(diào)用:

if (balance < amount) {
    throw new MyException(NOT_ENOUGH, '卡余額不足,目前可用余額:' + balance)
}

追求自由:反“錯(cuò)誤代碼”

與“系統(tǒng)異?!焙汀板e(cuò)誤代碼”努力嚴(yán)格限制系統(tǒng)輸出不同,自由派追求終極自由,對(duì)代碼和消息沒(méi)有任何限制。開(kāi)發(fā)人員可以隨意編寫(xiě)它們。

你可能會(huì)在多個(gè)地方看到“余額不足”的錯(cuò)誤,但每個(gè)錯(cuò)誤代碼都不同(可能由不同的人編寫(xiě),甚至是同一個(gè)開(kāi)發(fā)人員在不同時(shí)間或同一天心情不同)。

自由派的方法對(duì)于錯(cuò)誤提示有其好處。開(kāi)發(fā)人員可以自由定制個(gè)性化的提示內(nèi)容,當(dāng)系統(tǒng)遇到異常時(shí)可以快速定位錯(cuò)誤。然而,由于錯(cuò)誤代碼隨意編寫(xiě),對(duì)于依賴(lài)這些錯(cuò)誤代碼的調(diào)用方(系統(tǒng))不友好。一些系統(tǒng)需要根據(jù) API 返回的錯(cuò)誤代碼執(zhí)行特殊邏輯。當(dāng)調(diào)用方認(rèn)為 405 代表余額不足,但幾天后遇到 503 也表示余額不足時(shí),程序員的心肯定會(huì)崩潰。

中庸之道

我對(duì)異常處理的原則是:強(qiáng)制使用固定代碼和自定義消息。

要設(shè)計(jì)一個(gè)讓用戶(hù)和程序員都開(kāi)心的異常處理機(jī)制,首先要了解誰(shuí)需要使用這些信息。

異常信息的首要用戶(hù)是人,包括用戶(hù)(客戶(hù))和異常處理者(運(yùn)維人員、程序員)。

進(jìn)一步細(xì)分,異??梢苑譃闃I(yè)務(wù)異常和系統(tǒng)錯(cuò)誤。

業(yè)務(wù)異常是指業(yè)務(wù)流程中的異常場(chǎng)景,例如支付時(shí)卡余額不足導(dǎo)致支付失敗,使用優(yōu)惠券時(shí)發(fā)現(xiàn)不符合使用條件,以及用戶(hù)進(jìn)行未授權(quán)操作。這類(lèi)異常的觸發(fā)是用戶(hù)自己(而非系統(tǒng)),信息的受眾是用戶(hù)。因此,業(yè)務(wù)異常的信息提示必須關(guān)注用戶(hù)體驗(yàn)。優(yōu)秀的提示文本至少應(yīng)達(dá)到以下幾點(diǎn):

  1. 尊重用戶(hù),避免讓用戶(hù)感到被冒犯或嘲笑(請(qǐng)謹(jǐn)慎使用你認(rèn)為“幽默”的詞語(yǔ));
  2. 清晰,并包含觸發(fā)異常的關(guān)鍵信息(如余額不足時(shí)提示當(dāng)前余額);
  3. 引導(dǎo)用戶(hù),讓他們知道看完提示后該做什么;

第二類(lèi)異常是系統(tǒng)錯(cuò)誤,例如接口超時(shí),意外參數(shù)導(dǎo)致的程序崩潰,代碼邏輯錯(cuò)誤等。這類(lèi)異常的觸發(fā)是系統(tǒng)(或開(kāi)發(fā)系統(tǒng)的程序員),信息的受眾是程序員。因此,錯(cuò)誤消息對(duì)錯(cuò)誤類(lèi)型異常必須對(duì)程序員友好,允許他們快速識(shí)別問(wèn)題的原因并定位代碼中的位置。

我們通常談?wù)摰漠惓J侵稿e(cuò)誤類(lèi)型異常。這類(lèi)異常消耗了程序員的大部分精力,也值得優(yōu)化處理機(jī)制。

錯(cuò)誤類(lèi)型異常具有以下特點(diǎn):

  • 不可預(yù)測(cè)性:沒(méi)有程序員會(huì)主動(dòng)寫(xiě)錯(cuò)誤,但沒(méi)有系統(tǒng)是完全沒(méi)有錯(cuò)誤的。我們無(wú)法預(yù)測(cè)錯(cuò)誤來(lái)自哪里或會(huì)產(chǎn)生什么樣的錯(cuò)誤信息。
  • 難以定位:當(dāng)系統(tǒng)提示“余額不足”時(shí),我們很快知道這是用戶(hù)的卡沒(méi)有錢(qián)。然而,當(dāng)系統(tǒng)提示“參數(shù)類(lèi)型錯(cuò)誤”時(shí),我們通常感到困惑。
  • 可能涉及敏感信息:例如,當(dāng)出現(xiàn) SQL 操作錯(cuò)誤時(shí),可能會(huì)將整個(gè) SQL 語(yǔ)句暴露給外部。

綜合考慮

錯(cuò)誤提示對(duì)程序員友好,這可能意味著對(duì)用戶(hù)不友好。一些程序員利用這一點(diǎn),以“用戶(hù)體驗(yàn)”的名義將錯(cuò)誤提示信息轉(zhuǎn)換為“用戶(hù)友好”的提示。結(jié)果是每個(gè)人都感到困惑。

我的觀點(diǎn)是,錯(cuò)誤類(lèi)型異常根本不需要考慮用戶(hù)體驗(yàn)。

為什么?

因?yàn)橄到y(tǒng)出現(xiàn)錯(cuò)誤本身已經(jīng)是一個(gè)糟糕的用戶(hù)體驗(yàn)。用戶(hù)不會(huì)因?yàn)橄瘛癘ops,系統(tǒng)出錯(cuò)了”這樣的詞句而感覺(jué)好一點(diǎn)。用戶(hù)真正關(guān)心的是能盡快正常下單。

此時(shí)的當(dāng)務(wù)之急是快速修復(fù)錯(cuò)誤,因此提示文本的定位功能變得非常重要。純技術(shù)性的文本對(duì)用戶(hù)可能是胡言亂語(yǔ),但對(duì)程序員來(lái)說(shuō)非常有用。

然而,這并不意味著可以隨意給用戶(hù)錯(cuò)誤提示。如果為了便于定位而提示整個(gè)程序調(diào)用棧,雖然這可能不會(huì)進(jìn)一步降低用戶(hù)體驗(yàn),但會(huì)給人一種不專(zhuān)業(yè)的印象,過(guò)多的信息也意味著容易暴露敏感信息(如程序路徑、軟件版本、SQL 語(yǔ)句)。如果對(duì)方是黑客,你只能祈禱好運(yùn)。

此外,應(yīng)注意脫敏。在大多數(shù)框架中,當(dāng)數(shù)據(jù)庫(kù)操作失敗時(shí),它們的消息信息通常包含敏感信息,如 SQL 語(yǔ)句。這種信息不應(yīng)暴露在外。

因此,我們可以采用文本 + 日志結(jié)合的策略,在文本中包含關(guān)鍵信息,在日志中記錄詳細(xì)信息(包括調(diào)用棧)。

這也告訴我們另一件事:當(dāng)我們自己開(kāi)發(fā)公共庫(kù)時(shí),最好為該庫(kù)定義統(tǒng)一的基類(lèi)異常。這樣,想要以特殊方式處理該庫(kù)拋出的所有異常的用戶(hù)就不會(huì)手足無(wú)措。

此外,有些團(tuán)隊(duì)不想記錄業(yè)務(wù)異常的調(diào)用棧信息(“余額不足”的調(diào)用棧信息沒(méi)有太大意義)。我們可以在框架級(jí)別定義業(yè)務(wù)異常的基類(lèi):BusinessException,并在處理異常時(shí)不記錄此類(lèi)異常的調(diào)用棧信息。

另一個(gè)異常信息的用戶(hù)是系統(tǒng)。這包括其他服務(wù)、前端 JavaScript 腳本等。

我見(jiàn)過(guò)類(lèi)似的代碼:

try {
    ...
} catch (e) {
    switch (e.message) {
        case '用戶(hù)不存在':
            ...
        case ...
    }
}

如果有一天后端程序員心血來(lái)潮,將“用戶(hù)不存在”改為“用戶(hù)信息不存在”,系統(tǒng)就會(huì)崩潰。

創(chuàng)建這樣脆弱系統(tǒng)的程序員應(yīng)被釘在第1024柱羞恥柱上!

然而,在釘他們之前,我們應(yīng)該聽(tīng)聽(tīng)他們痛苦的呼聲:接口返回的錯(cuò)誤代碼混亂,已經(jīng)有八個(gè)不同的錯(cuò)誤代碼表示“用戶(hù)不存在”,將來(lái)也可能會(huì)更多。為了“系統(tǒng)穩(wěn)定”,最終決定基于消息進(jìn)行匹配。

好吧,那讓我們一起釘所有后端程序員!

系統(tǒng)只應(yīng)關(guān)注錯(cuò)誤代碼,而不是其他內(nèi)容。與消息可以自由變化不同,錯(cuò)誤代碼應(yīng)具有相當(dāng)?shù)姆€(wěn)定性。

在同一系統(tǒng)中,如果 406 表示“用戶(hù)不存在”,則不應(yīng)使用其他值(如 604)表示相同含義。

此外,代碼面向系統(tǒng)的特性還要求代碼定義一類(lèi)異常(而不僅僅是一種異常)。例如,“訂單創(chuàng)建失敗”是一類(lèi)異常,不同的失敗原因在業(yè)務(wù)代碼中有不同的消息,但共享相同的代碼。

然而,人類(lèi)對(duì)數(shù)字不敏感。每個(gè)程序員都不能確保寫(xiě) throw new Exception('用戶(hù)不存在', 406) 而不是 throw new Exception('用戶(hù)不存在', 604)。

因此,有必要通過(guò)定義常量錯(cuò)誤代碼將數(shù)值轉(zhuǎn)換為文本:

const USER_NOT_EXISTS = 406

代碼中只能使用錯(cuò)誤代碼常量。

throw new Exception('用戶(hù)不存在', USER_NOT_EXISTS)

禁止使用文字常量。

然而,上述 throw 語(yǔ)句并不理想。首先,默認(rèn)的 Exception 類(lèi)型沒(méi)有業(yè)務(wù)語(yǔ)義。其次,如果開(kāi)發(fā)人員堅(jiān)持使用數(shù)字常量,誰(shuí)也無(wú)法阻止。更好的方法是為每種異常類(lèi)型定義單獨(dú)的異常類(lèi),只允許傳遞消息,并在內(nèi)部綁定代碼。

// 用戶(hù)不存在。
class UserNotExistsException extends Exception { 
    constructor(message) {
        super(message)
        
        this.code = ErrCode.USER_NOT_EXISTS
    }
}

使用:

if (!User.find(uid)) {
    // 這種寫(xiě)法更有表現(xiàn)力,開(kāi)發(fā)人員不需要關(guān)注錯(cuò)誤代碼
    throw new UserNotExistsException(`用戶(hù)不存在(uid:${uid})`)
}

異常處理機(jī)制的示例

首先,總結(jié)中庸之道的異常處理機(jī)制的特點(diǎn):

  • 強(qiáng)制開(kāi)發(fā)人員編寫(xiě)異常描述文本;
  • 整個(gè)項(xiàng)目要求使用統(tǒng)一的錯(cuò)誤代碼定義;
  • 為業(yè)務(wù)異常定義單獨(dú)的基類(lèi);
  • 對(duì)敏感信息進(jìn)行脫敏處理;

錯(cuò)誤代碼的統(tǒng)一定義:

const OK = 200
const SYS_ERR = 500
const NOT_FOUND = 404
const NOT_ENOUGH = 405
const USER_NOT_EXISTS = 406
...

業(yè)務(wù)異常基類(lèi):

class BussinessException extends Exception {
    ...
}

異常類(lèi)定義:

class UserNotExistsException extends BussinessException {
    constructor(message) {
        super(message)
        
        this.code = ErrCode.USER_NOT_EXISTS
    }
}

業(yè)務(wù)層使用:

if (!User.find(uid)) {
    throw new UserNotExistsException(`用戶(hù)不存在。(uid:${uid})`)
}

基礎(chǔ)控制器捕獲異常:

class BaseController {
    ...
    
    errorHandler(err) {
        // 是否是業(yè)務(wù)異常
        const isBussError = err instanceof BussinessException
        // 是否是數(shù)據(jù)庫(kù)異常
        const isDBError = err instanceof DBException
        // 生成用于跟蹤異常日志的隨機(jī)字符串
        const flag = isBussError ? '' : random()
        
        let message = err.message
        if (isDBError) {
            // 數(shù)據(jù)庫(kù)異常,脫敏處理
            message = `數(shù)據(jù)異常(flag:${flag})`
        } else if (!isBussError) {
            // 非業(yè)務(wù)異常記錄標(biāo)識(shí)符
            message += `(flag:${flag})`
        }
        
         // 記錄錯(cuò)誤(日志應(yīng)記錄原始消息)
         log(err.message, isBussError ? '' : err.stackTrace(), flag)
         
         // 返回給調(diào)用方
        this.response.sendJSON({"code": err.code, "message": message})
    }
    
    function log(message, stackTrace, flag) {
        ...
    }
    ...
}

約定機(jī)制

即使框架提供了全面的異常處理機(jī)制,你仍然無(wú)法阻止開(kāi)發(fā)人員編寫(xiě)這樣的代碼:

if (!User.find(uid)) {
    throw new Exception('系統(tǒng)異常', 500)
}

一行代碼將使你回到原點(diǎn)!

因此,異常處理機(jī)制是基于約定的(團(tuán)隊(duì)約定)。

技術(shù)負(fù)責(zé)人必須為所有成員提供系統(tǒng)培訓(xùn),并公開(kāi)建立團(tuán)隊(duì)代碼標(biāo)準(zhǔn)。他們應(yīng)堅(jiān)決拒絕不符合標(biāo)準(zhǔn)的 pull 請(qǐng)求,并與那些屢教不改的人進(jìn)行“黑房對(duì)話”。

責(zé)任編輯:武曉燕 來(lái)源: 大遷世界
相關(guān)推薦

2023-04-26 08:55:30

2011-10-25 09:24:08

2018-02-23 09:55:12

程序員壓迫Python

2025-01-22 07:00:00

C++11構(gòu)造函數(shù)C++

2020-05-22 15:16:45

遠(yuǎn)程工作辦公互聯(lián)網(wǎng)

2015-08-10 10:26:08

2011-11-08 11:22:35

技術(shù)周刊

2021-07-01 05:17:52

Windows 11操作系統(tǒng)微軟

2023-08-29 06:50:01

Javamaven

2025-10-27 08:46:00

2014-03-06 09:23:19

Git服務(wù)器Github

2025-10-26 14:50:25

AI人工智能ChatGPT

2012-11-12 12:03:26

臺(tái)式機(jī)Mac聯(lián)想

2013-03-08 09:54:25

2020-07-06 14:40:28

攜號(hào)轉(zhuǎn)網(wǎng)運(yùn)營(yíng)商服務(wù)

2021-03-03 14:55:10

開(kāi)發(fā)MySQL代碼

2017-05-08 11:01:54

交通馬斯克地下隧道

2013-05-06 17:08:00

Linux操作系統(tǒng)異常處理

2012-11-05 13:59:12

WebFdSafeJS

2019-10-09 10:06:48

容器監(jiān)控軟件
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)