19.4 · What NOT to rewrite (public contracts & intentional typos)
บทนี้คือ เบรก ของ modernization — รายการที่ deep scorecards ระบุว่าเป็น public contract (ENUM, env name, file path, GraphQL operation, magic number) ซึ่งดู "ผิด" แต่มี consumer ใน frontend / DB / deployment พึ่งอยู่
/ This chapter is the brake on modernization — items deep scorecards list as public contracts. They look wrong but have live consumers; renaming them casually breaks production.
กฎเหล็ก จาก vtrc-api/10 Public contract: เจอแล้วไม่แน่ใจ — ถามทีม HR/i3 ก่อนเสมอ ห้ามเดาใน PR security/correctness
ทำไมต้องมีบทนี้
ทีมที่เข้ามาใหม่มักเปิด PR แนว:
- แก้ typo
PEDDING_CANCLE→PENDING_CANCEL - rename
from/→form/ - unify
wdHospitals/WDHospitals - ลบ
@Public()บน dropdown เพราะ "ไม่ควร public"
ทุกอันนี้อาจถูกต้องในระยะยาว แต่ ห้ามทำใน PR เดียวกับ rotate secret / ลบ backdoor / แก้ SQLi — blast radius คนละแบบ และ rollback ยาก
ขอบฟ้าที่ถูกสำหรับ rename คือโปรเจกต์แยก มี migration + dual-read + sync frontend + ops env พร้อมกัน
A · vtrc-api — สัญญาที่ต้องคงแม้ดูผิด
ที่มา: vtrc-api/10 § Public contract
| รายการ | ที่ตั้ง | เหตุผลที่ต้องคง | ถ้าจะแก้จริงต้อง |
|---|---|---|---|
PEDDING_CANCLE | models/core/leave/leave.js:59 | ENUM sync ลง DB หลายตาราง | migration ครบทุกตาราง + ทุก client |
DRAF | config.js statusHrmi | mapping จาก cu-central-api | sync ทั้งสองฝั่ง |
CONINCLE_USERNAME | config.js, deployment .env | ชื่อ env ที่ pipeline ใช้ | coordinate ops เปลี่ยนทุก env |
Moblie filename | lib/controllers/auth/authSessionMoblie.js | import path หลายไฟล์ | rename + update ทุก import ใน PR ใหญ่แยก |
KEY_PLASMA, KEY_HR | config.js:153-154 | UUID org จริงใน prod DB | sync ทุกที่ที่ hardcode |
corss | index.js:76 | typo key ของ CORS config | แก้พร้อม SEC-VTRC-API-06 ทั้งชุด — dual-read key ชั่วคราวถ้าต้อง |
signInByPass (centralize controllers) | centralize callers | บาง flow อาจตั้งใจ | เช็คทุก caller ก่อนลบ — คนละตัวกับ /auth/signinbypass ของ cu-central ที่ต้องลบใน 90 วัน |
tr ใน redis.controller.js:16 | redis wrapper | landmine ถ้า revive cache | แก้ก่อนเปิด Redis อีกครั้งเท่านั้น |
responError / getResapeMessage | กระจายทั้ง repo | import หลายร้อยจุด | codemod แยก ไม่ปน feature |
APP_NO = 1,2,3,5 (ไม่มี 4) | config.js | cron leader election | เปลี่ยนลำดับไม่ได้โดยไม่ redesign election |
clouse | lib/clouse.js | file name ใน import | แก้ logic ได้; rename ไฟล์ต้องแยก |
สิ่งที่ต้องแก้ใน 90 วัน vs สิ่งที่ต้องคงชื่อ
| แก้พฤติกรรม (ทำ) | คงชื่อ/สัญญา (อย่า rename) |
|---|---|
ลบ default JWT_SECRET='secret' | ชื่อ env JWT_SECRET เอง |
| ปิด command injection qpdf | path ของ routes ที่มี client เรียก |
ใส่ auth ที่ /file/:id/download | ชื่อ param :id |
| แก้ CORS ให้ allowlist ทำงาน | พิจารณา dual-support corss+cors ชั่วคราว |
B · vtrc-web — public contract
ที่มา: vtrc-web/07 § Public contract
| รายการ | ที่ตั้ง | เหตุผล |
|---|---|---|
from/ folder typo | components/user/from/ | import ทุกที่ — QUAL-VTRC-WEB-01 ระบุห้ามแก้ import ลวก ๆ |
AntD v3 getFieldDecorator | กระจาย forms | ไม่มีใน v4 — upgrade = rewrite ทุก form เป็นโปรเจกต์แยก |
moment.js th_TH + Buddhist year | กระจาย | เปลี่ยน library ต้อง sync conversion ปี พ.ศ. |
| GraphQL operation names PascalCase | queries.js, mutations.js | backend resolver matching |
Mutations.login (ไม่มี suffix) | mutations.js | ต่างจาก Mutations.loginBackoffice ของ rc-backoffice — ห้าม unify ชื่อ |
apiKeyNews() | utils/apiKey.js | คืน device key ของ WEB APP_TYPE |
modernization ที่ทำได้โดยไม่แตะสัญญาเหล่านี้
จาก vtrc-web/07 ROI:
dompurify— ทำได้ทันที- CSP header — ทำได้ทันที
- Code splitting / AntD tree-shake — ไม่ต้อง rename folder
- HttpOnly cookie — เปลี่ยนที่เก็บ token ไม่ต้องเปลี่ยนชื่อ mutation
C · vtrc-rc-backoffice — public contract
ที่มา: vtrc-rc-backoffice/07 § Public contract
| รายการ | ที่ตั้ง | เหตุผล |
|---|---|---|
TheamCss route + folder | App/index.js:23, pages/TheamCss/ | typo ที่ import ทั่ว; QUAL-VTRC-RC-01 ห้ามแก้ลวก; ใช้เป็น CSS preview tool |
Mutations.loginBackoffice | graphql/mutations.js | suffix BO — แยก flow จาก user-side |
Queries.refreshTokenBO | graphql/queries.js | refresh แยก |
getOrganization() จาก sessionStorage | utils/auth.js | reset sessionStorage = login fail |
apiKeyNews() → WEB_ADMIN | utils/apiKey.js | backend map apiKey → APP_TYPE |
AntD v4 Form.useForm() ปน v3 getFieldDecorator | กระจาย | migration ทีละ form — ห้าม mix ใน form เดียว |
| 2way-meeting-backoffice embedded SSO | pathLogin pattern | เปลี่ยน handoff ต้องคุย 2way |
ทำได้ทันทีโดยไม่ rename
vtrc-rc-backoffice/07 ROI: ลบ .env + rotate, POST body SSO, dompurify, CSP, lazy Stimulsoft — ไม่ต้องแตะ TheamCss
D · benefit-api — public contract
ที่มา: benefit-api/06 § Public contract
| รายการ | ที่ตั้ง | เหตุผล |
|---|---|---|
docType magic numbers 1–5 | payments / disaster / disbursement | frontend ส่งเลขเหล่านี้ |
Triple DB names db, dbHRMI, vtrcDB | configuration.ts | injection token — rename กระทบทุก service |
SECRET_KEY env var name | configuration.ts:5 | pipeline ใช้ชื่อนี้ — เปลี่ยนค่าได้; เปลี่ยนชื่อต้อง coordinate |
wdHospitals vs WDHospitals casing | entity vs raw SQL | คนละ schema — ห้าม unify โดยไม่ migrate |
SFTP paths upload/fmis/bgdoc/ ฯลฯ | exportpaymentreport.service.ts | FMIS server คาด path เหล่านี้ |
@Public() บน dropdown | dropdown.controller.ts | frontend อาจเรียกตอนยังไม่ login — ลบต้อง verify |
แยกให้ชัดจากสิ่งที่ต้องลบ @Public()
| เก็บไว้ก่อน (verify กับ frontend) | ลบใน 90 วัน |
|---|---|
dropdown @Public() | wdHospitals add/update/detail (SEC-BENEFIT-03) |
batchJob / upload public (SEC-BENEFIT-04/05) |
E · pms-api — สิ่งที่อย่าทำพัง / อย่า "cleanup" ผิดจุด
ที่มา: pms-api/06 § สิ่งที่ทำได้ดีแล้ว และ Low findings
| รายการ | คำแนะนำ |
|---|---|
*History audit tables | ห้ามลบเพื่อลดตาราง |
Parameterized sp_get_employeeSalary_* | ห้ามแทนด้วย raw string SQL "ให้อ่านง่าย" |
ThrottlerGuard | ห้ามถอดตอนเปิด auth |
ValidationPipe forbidNonWhitelisted | ห้ามผ่อนเพื่อให้ frontend ส่ง field เกินโดยไม่ตั้งใจ |
QUAL-PMS-06 typo periodExtesnd filename | Low — rename เป็นโปรเจกต์แยกถ้าจำเป็น |
CONF-PMS-01 ชื่อ env JWT_EXPIRE_IN / REFRESH_TOKEN_SECRET_KEY | ใส่ค่าใน secret manager; อย่าเปลี่ยนชื่อโดยไม่แจ้ง deploy |
ห้ามรีบลบ @Public() บน getOrgUnit / list โดยไม่ inventory caller | ต่างจาก getEmployeeSalary ที่ ROI #1 ชัด |
F · 2way-api — สิ่งที่อย่าทำปน PR backdoor
ที่มา: 2way-api/04
| ทำใน 90 วัน | อย่าปนใน PR เดียวกัน |
|---|---|
ลบ i3admin block | ลบ dead modules ทั้งก้อน (QUAL) |
| ลบ secret fallback | รวม Node + package manager (QUAL-04/05) |
ignoreExpiration: false | redesign โครง announce ทั้งหมด |
| Parameterized SQL | เปลี่ยนชื่อ column ใน MSSQL |
คุย frontend/LINE ก่อน ถอด @Public() บน complaints / LINE / announce mutations (SEC-2WAYAPI-04) — การถอดถูก แต่ timing ต้องมี client พร้อมส่ง JWT
G · Shared platform contracts (ข้าม repo)
| สัญญา | Consumer | หมายเหตุ |
|---|---|---|
Device apiKey → APP_TYPE (WEB, WEB_ADMIN, MOBILE, …) | ทุก legacy frontend + vtrc-api config | เปลี่ยนค่า key = rotate; เปลี่ยนชื่อ header กระทบทุก client |
GraphQL endpoint จาก checkLinkURL() / window.location.href | vtrc-web apollo config | อย่าเปลี่ยนเป็น .env-only โดยไม่ dual-support |
SECRET_KEY ชื่อตัวแปร ชุด domain Nest | benefit / recruitment / ai-recruitment / pms | เปลี่ยนค่าพร้อมกันได้; เปลี่ยนชื่อต้องพร้อม pipeline ทุกตัว |
| Buddhist year ใน UI | web + backoffice | อย่าบังคับ CE ปี ค.ศ. ใน form HR โดยไม่ผ่าน product |
H · Decision matrix — เมื่อไหร่ถึงจะ "rewrite ชื่อ"
ใช้ตารางนี้ก่อนเปิด rename PR:
| คำถาม | ถ้าคำตอบเป็น "ไม่" |
|---|---|
| มี inventory ทุก consumer (code + DB + env + FMIS + mobile) ครบหรือยัง | หยุด |
| มี dual-read / dual-write ช่วงเปลี่ยนหรือยัง | หยุด |
| มี rollback ที่ไม่ต้อง restore DB จาก backup หรือไม่ | หยุด |
| PR นี้มี security fix อื่นปนอยู่หรือไม่ | แยก PR |
| ผ่าน 19.1 DoD แล้วหรือยัง (ถ้าเป็น cosmetic) | เลื่อนหลัง emergency |
I · สรุปสั้นสำหรับ reviewer
เมื่อ review PR ที่อ้าง Vol 19:
- ถ้าแตะรายการในตาราง A–D → ขอ decision doc หรือ reject
- ถ้าลบ
@Public()→ แยก dropdown vs write/PII - ถ้าแก้ typo ภาษาอังกฤษในชื่อไฟล์/ENUM → ถามว่า migration พร้อมหรือยัง
- ถ้า "cleanup ตอนแก้ security" → ขอแยก commit/PR
Modernization ที่ดีคือ เปลี่ยนพฤติกรรมอันตราย โดยคงสัญญาที่โลกภายนอกพึ่ง จนกว่าจะมีโครงการย้ายสัญญาอย่างจงใจ
/ Good modernization changes dangerous behavior while keeping external contracts stable until an intentional contract-migration project exists.