新项目一开我现在很少第一反应去建 项目了。不是说不行, 也不是Flask已经不流行了, 而是众多后端项目已经变样了: 不再是一个带有几个页面的后台管理系统, 而是出现了大量接口, 具备诸多回调, 包含各类任务, 有AI服务, 还有内部平台以及网关转发。这种项目 上来就顺手。我所言的“反超”, 并非指于全部公司、全部老项目里面把和Flask给干掉了 这种情况。老系统并非这般轻易就能替换。真正出现反超的是新项目里的默认选择顺序这一情况。以前问 Web 框架脑子里基本是做大项目。Flask 做小服务。现今, 好多人会先问出这样一句话: 这个接口需不需要进行类型校验, 要不要有自动文档, 有没有异步请求, 有没有模型入参。这一问 就站到前面了。2025 开发者调查里, 有一项在 Web 框架里增长被明显提到, 并且满意度也颇佳。在开发者调查里, 开发者期望 IDE 增强支持的框架中, 它也位列于和 Flask 之前。在 PyPI Stats 上能够看到, 其下载趋势仍呈上升态势, 更为稳定, 而 Flask 相对没那么迅猛。此方向大体已然明晰。我平时看一个 Web 项目第一眼会看接口入口。Flask 代码经常长这样from flask import Flask, request, jsonify app Flask(__name__) app.post(/orders/query) defquery_orders: body request.get_json or {} user_id body.get(user_id) page int(body.get(page, 1)) ifnot user_id: return jsonify({code: 400, msg: user_id required}), 400 rows load_orders(user_iduser_id, pagepage) return jsonify({code: 0, data: rows})这段不存在问题, Flask所具备的正是这种感受, 轻盈, 干脆, 对于过多之事不予过多干涉。但问题也在这里。网页是不是数字, 是不是空字符串形成的, 返回结构是不是一致的, 接口文档由谁进行维护工作呢, 这些都得要你自己去补充完整。小型服务问题不大, 但是接口数量一旦增多, 这些需要“自己补”的部分最终都会分散到各个不同的文件当中去的写法我更愿意在新接口里这么落from typing import Annotated from fastapi import FastAPI, Header, HTTPException from pydantic import BaseModel, Field app FastAPI classOrderQuery(BaseModel): user_id: str Field(min_length1) page: int Field(default1, ge1, le200) only_unpaid: bool False classOrderItem(BaseModel): order_no: str amount: int status: str app.post(/orders/query, response_modellist[OrderItem]) defquery_orders( query: OrderQuery, trace_id: Annotated[str | None, Header(aliasx-trace-id)] None, ): if query.user_id.startswith(tmp_): raise HTTPException(status_code403, detailtemp user blocked) print(ftrace{trace_id} query_orders user{query.user_id} page{query.page}) return load_order_items( user_idquery.user_id, pagequery.page, only_unpaidquery.only_unpaid, )这段代码我比较放心。并非因它呈现出崭新之态, 而是诸多繁杂的脏活为由框架预先予以限控了: 诸如入参校验, 类型的转换, 响应的结构, 还有文档。官方所提供的文档亦已然明显地将类型注解, 自动生成的文档, 这些均当作核心的能力。这里有个变化挺关键。往日时期, 撰写接口时, 诸多项目秉持“先运行起来再作考量”的态度。当下情形已然不同, 前端需依据接口文档展开联调工作, 测试阶段得依据其实施数据构造工作, 且AI服务常常会将模型的输入及输出进行串联整合。若你依旧依赖口头约定字段, 早晚必定引发争吵。讨巧的地方就在这它把 类型提示这套东西吃得比较狠。写 int 它就按 int 校验。写 它就按模型解析。写 它就把返回也管起来。这不是语法糖这会影响接口质量。但是, 我同样不赞同, 那一种这般表述, 即“出来之后, 已然无用了”的说法。这种判断太过轻巧, 一眼便知未曾维护过后台系统。还是很能打尤其是这种项目有用户体系。有权限。有后台管理。有表单。有 ORM。有一堆运营同学每天要查数据、改状态、导 CSV。此种场景, 你若采用从零积攒的方式, 并非不可行, 不过你需自行补充诸多内容。官方之定位, 本就是高层 Web 框架, 许多 Web 开发里的繁杂之事, 直接由它帮你处理掉。我曾见识过某些团队, 当听闻新项目很热门, 即刻便着手将管理后台拿来进行编写, 最终自行打造登录模块、打造权限体系、打造菜单设置、打造审计日志功能、打造操作记录流程。写到第三周就开始怀念 Admin。这个时候不是框架选型先进是没算账。Flask 也一样。Flask 最大价值并非“老”, 而是克制, 官方文档称其为轻量 WSGI Web 框架, 适合快速开启, 也能够扩展至复杂应用, 此描述颇为准确。我一般会在两种地方继续用 Flask。一种是尺寸特别小的用于内部的工具, 像是临时进行连接, 将数据予以转发, 实施健康检查的挂载操作。还有一种情况是, 老项目已然处于稳定状态了, 且团队成员彼此也都熟悉了。倘若你非要强行把Flask替换成, 这样做往后呢, 除了能让简历看着较为美观外, 大概率在线上会面临更高的风险。真正该警惕的是这种 Flask 项目app.post(/callback/pay) defpay_callback: payload request.json save_raw_callback(payload) if payload[status] SUCCESS: mark_order_paid(payload[orderNo], payload[paidTime]) returnok这代码第一眼看着能跑我第一眼就不太信。.json 为空怎么办字段改名怎么办重复回调怎么办金额有没有校验支付回调这种接口不是能返回 ok 就完事了。换成 我至少会先把边界拦住from decimal import Decimal from pydantic import BaseModel, Field classPayCallback(BaseModel): order_no: str Field(min_length8) amount: Decimal Field(gt0) status: str paid_at: str sign: str app.post(/callback/pay) defpay_callback(event: PayCallback): ifnot verify_pay_sign(event): raise HTTPException(status_code401, detailbad sign) if event.status ! SUCCESS: record_pay_noise(event.order_no, event.status) return {ok: True} changed mark_paid_once( order_noevent.order_no, amountevent.amount, paid_atevent.paid_at, ) print(fpay_callback order{event.order_no} changed{changed}) return {ok: True}这里不是 多神而是它逼着你把接口边界写出来。对于字段究竟是什么, 类型到底是什么状况, 失败之后会怎样去报, 文档大致呈现出什么样的样子, 这些方面都相对比较清楚。这些年生态实际上一直朝着“工程化”发展 , 类型提示 , 异步IO , 自动生成客户端 , 这些事物组合在一起 , 方才将其推动起来。和 Flask 当年解决的是另一批问题。解决的是我想快速做一个完整 Web 应用。Flask 解决的是我想少受框架限制自己拼。要去搞定的是: 我期望迅速制作出一批具备可靠性的 API, 并且这些 API 其最佳状态是自然而然带有类型、校验以及文档的。所以现在选型我会这么看。后台系统、内容管理、运营平台 依然稳。临时服务、小工具、老项目扩展Flask 依然够用。新的 API 服务, AI 接口数据中台, 前后端分离项目, 我会优先去看它。别为了追新换框架也别因为老框架熟就一直不动。框架这东西最后还是要落到代码现场。接口进入的参数是不是混乱无序, 异常情况有没有被妥善接住, 文档内容与代码是否保持一致, 线上出现问题后能否依据 trace 顺利追溯回来。这些地方 确实更贴近现在 后端的工作方式。和 Flask 还行吗当然还行。