PHP 怎么决策记录

wen PHP项目 1

本文目录导读:

PHP 怎么决策记录

  1. 维度一:架构决策记录(团队协作 / 文档)
  2. 维度二:业务决策日志(运行时追踪)
  3. 维度三:技术选型决策(代码注释里的决策)
  4. 总结给 PHP 开发者的建议

在 PHP 开发中,“决策记录”通常指架构决策记录(ADR,Architecture Decision Record),或者是业务逻辑中的决策日志,我会从这两个维度分别给出落地方案。


架构决策记录(团队协作 / 文档)

这是给开发者看的,记录“为什么”要这么写代码,推荐使用轻量级的 Markdown 文件,放进代码仓库。

标准目录结构(ADR 模式) 在项目根目录创建 docs/adr/ 文件夹,每个决策一个文件,用递增数字命名:

docs/adr/
├── 0001-use-vite-as-build-tool.md
├── 0002-adopt-repository-pattern.md
└── 0003-use-redis-for-session-storage.md

标准模板(避免写废话)

# 3. 使用 Redis 替代文件存储 Session
- 日期: 2025-05-20
- 状态: 已接受 (Accepted)  // 可选:提议/已接受/已废弃
- 决策者: 张三、李四
## 背景
当前使用 PHP 原生 `$_SESSION`,在负载均衡多机部署时,会话数据不同步,导致用户频繁掉线。
## 决策
采用 Redis 作为会话存储驱动(使用 `phpredis` 扩展)。
## 后果
- 正面: 支持横向扩展,会话读取速度快。
- 负面: 运维需额外维护 Redis 高可用;若 Redis 宕机,所有用户会话失效。
## 替代方案
- 使用数据库存储(太慢)。
- 使用 JWT 无状态 Token(改动前端较大,暂缓)。

实施要点

  • 写“为什么”:记录当时考量的技术约束和业务场景,别记“用了什么函数”。
  • 状态流转:如果方案被推翻,不要删除文件,将原文件状态改为“已废弃”,并新建 0004 文件说明新方案,保留历史脉络。

业务决策日志(运行时追踪)

这是给业务方或排查问题时看的,记录用户或系统在运行时的关键操作选择。

建议不要直接 error_log() 乱写,应该结构化地记录到日志表或文件。

核心类封装(决策记录器) 使用单例模式注册到容器,方便在任何地方调用。

<?php
namespace App\Services;
use Psr\Log\LoggerInterface;
class DecisionLogger
{
    private array $context = [];
    public function __construct(private LoggerInterface $logger) {}
    /**
     * 开始记录一次业务决策过程
     */
    public function start(string $processName, string $userId, array $initData = []): void
    {
        $this->context = [
            'process' => $processName,
            'user_id' => $userId,
            'start_time' => microtime(true),
            'steps' => [],
        ];
        $this->log('决策开始', $initData);
    }
    /**
     * 记录每一步判断
     */
    public function step(string $decisionPoint, string $result, array $reason = []): void
    {
        $this->context['steps'][] = [
            'point' => $decisionPoint,
            'result' => $result,
            'reason' => $reason,
        ];
        // 写入系统日志或异步队列
        $this->logger->info("决策节点: {$decisionPoint} -> {$result}", $reason);
    }
    /**
     * 结束记录并归档
     */
    public function finish(string $finalResult): void
    {
        $elapsed = microtime(true) - $this->context['start_time'];
        // 这里可以推送到 ElasticSearch 或存入数据库表
        $this->logger->notice("决策完成: {$finalResult}", [
            'process' => $this->context['process'],
            'steps' => $this->context['steps'],
            'duration_ms' => round($elapsed * 1000, 2),
        ]);
    }
}

在业务逻辑中的使用(优酷案例:风控/推荐/优惠券)

假设你在做订单折扣计算:

public function calculateDiscount(Order $order, DecisionLogger $logger): float
{
    $logger->start('order_discount', $order->user_id, ['order' => $order->id]);
    // 如过用户是 VIP
    if ($order->user->isVip()) {
        $logger->step('vip_check', '通过', ['vip_level' => $order->user->vip_level]);
        $rate = 0.9;
    } else {
        $logger->step('vip_check', '未通过', ['err' => '用户非VIP']);
        $rate = 1.0;
    }
    // 如果金额大于 1000 使用大额券
    if ($order->total > 1000) {
        $logger->step('threshold_check', '命中大额券', ['total' => $order->total]);
        $rate -= 0.05;
    } else {
        $logger->step('threshold_check', '未达门槛');
    }
    $final = $order->total * max(0.1, $rate);
    $logger->finish("最终折扣: {$final}");
    return $final;
}

存储方案选择(重要)

场景 推荐方案 原因
低并发、内部系统 写入 MySQL 日志表 好查好看,配合后台管理界面过滤用户ID
高并发、核心链路 写入 InfluxDB/TDengine 时序数据库或 Redis Stream 异步批量落盘,不阻塞主流程
需要全文检索 直接输出 JSON 到 ELK (ElasticSearch) 后续可按关键词搜“为什么没送券”

技术选型决策(代码注释里的决策)

在代码里遇到关键的 IF/ELSE 或设计模式选择时,除了魔法数字,可以加一个标准化的注释块:

/**
 * 决策:为什么不直接用 `==` 判断?
 * 原因:`0 == "foo"` 在 PHP 8.0 之前的版本会返回 true,这里必须严格比较类型。
 * 成本:牺牲少量代码简洁性,换取类型安全。
 */
if ($inputValue === 0) { // 严格的 Int 比较
    // ...
}

总结给 PHP 开发者的建议

  1. 团队决策:用 ADR 放进 docs/adr/,GIT 提交时一起走 PR 审核,记录演进史。
  2. 运行决策:封装一个 DecisionLogger 类,关键节点埋点,日志格式统一为 point -> result,方便排查“为什么走到了这个分支”。
  3. 核心原则:决策记录是给未来的自己看的,犯过的错不重复,想过的理由不重想。

抱歉,评论功能暂时关闭!