产品线上交付阶段出现的两次显著Bug分析

前言 近期,部门内部的A项目在线上使用时出现了两次较为明显的Bug。作为负责A项目需求梳理、原型设计和功能验收的产品经理,我将从这一角色出发进行思考和总结。 问题描述 前端页面编辑一个输入框时,显示“入参错误”。 前端页面修改模块A时,模块B和模块C的同一参数输入框出现联动,都变为了模块A的值。 前端页面调用的字段和后端定义的功能没有对应上。 问题分析 问题一的主要原因是后端相关的功能只发布在了开发和测试环境,而正式环境没有发布。 问题二的原因是模块A、B和C中同一参数是抽取复用的,但前端在处理时,没有使用对象解构赋值 {...parameter} 来创建一个新的对象副本,而是直接引用了同一份 parameter 对象导致。 问题三的原因是需求文档显示,前端在开发时,已经和后端同事约定好参数字段一一对应,但在后端实现时,沟通上面信息没有同步到位,字段和预期的不一致,导致功能出现异常。前端开发人员没有真正从用户角度出发,验证功能是否完善。产品交付时,产品经理没有依据需求文档进行验收,做好输入和输出两个关键部分,形成闭环。 经验教训 当需求中出现大量专业性较高的参数字段时,需要在需求文档中说明清楚,确保信息同步,毕竟这些不是像username、mobile、email等常识类的字段。 前端在开发功能后,通常容易陷入只要保证自己实现的功能页面增删改查没有问题,就可以了。很少多想一步,从用户角度出发,模拟用户的使用场景,然后再次确认功能是否正常,这个意识还是很重要的。因为一个功能模块不仅仅涉及前端,还有后端,功能的验收要结合后端进行,这个时候没办法把职责划分那么清晰,只能前端主导,后端配合,把整个功能模块验证流程都走一遍,而不是浮于表面。 在部门里,期望产品同事参与到项目中,具体除了整理需求文档和原型设计,最终的交付也是很重要的,这就需要产品切实参与到版本功能的验收,确保和预期的需求一致。这方面曾涉及到对研发的信任问题,其实和信任没有关系,能力再出众,功能做得再好,如果理解错了需求,最终交付的不是需求的本意,那也是有问题的,所以产品参与最终版本的功能确认,是很关键的一环。就好像封装一个函数,输入和输出,先不管函数里面到底是怎么实现的,最终还是以函数输出的结果为重,函数给的结果都不对,里面用了多好的技术和巧妙构思,都显得苍白无力。所以,产品对于每一个需求点,需求->验收,应形成闭环。 潜意识里还是觉得是内部的项目,导致重视程度不够,而且,忙并不是借口,对于负责A项目的产品工作来说,验收形成闭环这一块确实懈怠了,本就应该按标准的项目流程去走,必须做的就要去做,没办法省略掉。 预防措施 验收环节需提供详细测试用例,并且分为三个环节:研发自测、项目负责人验收和产品交付验收,基本覆盖了这个功能的开发人员、这个项目的负责人和这个需求的提出人。 发版节奏放缓,确保各个环节都做到位。贪图求快,功能质量出现问题,工作结果还是大打折扣的。

2025-01-02 · 1 分钟 · 22 字

2025年终总结:你应该成为解决问题的人,而不应成为问题本身

2025 年一下子就到尾声了,总是感叹这种时光飞逝,没有任何触感,无形无味,无声无息。 回望这一年,在公众号上,一共发表了 106 篇文章,2024 年是 63 篇,多了 43 篇。 今年,在公众号的内容上,做出了一些调整。 首先,是结束了 ARTS 打卡活动,然后,整理了 2022 年写的项目管理实战笔记,接着,新增了电影观后感、书籍读后感系列,还有产品推广方面、AI 相关应用的文章。 今年大部分的工作,都在做产品推广,涉及到产品营销、官网 SEO 优化等各方面的内容,就是怎么能让用户尽可能多的看到我们的产品。 所以,从产品推广这个角度,来审视我这个公众号,就会发现问题一大堆。 真正要经营好公众号,是应该要选好一个方向的,这样受众才能精准定位,比如前端开发、项目管理、团队管理、电影观后感、书籍读后感、产品推广等。 每一个方向都可以是一个公众号。 但是,我把它们都揉在一起了,什么东西都往里扔,内容千奇百怪、五花八门。 好像什么都有,这种是很难运营的,定位不清,意味着身份不明,很难让人有记忆点。 而且,公众号的名称就有问题,不够具体,没有带上关键词,比如前端、项目、管理、产品、SEO、电影、阅读、程序员等。 这些关键词,理应都要在公众号的名称中体现,这样,用户在搜索内容的时候,才会命中关键词,才有可能找到你的公众号。 还有就是,你要围绕公众号的受众,来打造内容,从题材、文章风格、个人形象塑造等各方面的细节都要想明白和做到位。 最最最核心的一点是,你是否给你的用户带来了他们想要的东西? 现在是短平快时代,很多人越来越没有耐心了。 就像我很多篇产品推广的文章中说的,用户的注意力是有限的,没那么多时间和精力,深入地研究你的长篇大论。 我只想要能引起我兴趣的,立马能抓住我眼球的东西。 所以,公众号的文章,太长其实是非常不利的。 所以,整个公众号的内容,就变成大杂烩了。 我是知道存在这些问题的,但是,自己工作的内容,本身就不只是某个方向,什么都有一点。 章总还标榜自己是什么 “十全武功”,市面上还讲什么 “π”、“T” 型人才,我经常开玩笑说,我是 “米” 型人才,连六边型都不够用了。 虽然很容易变成 “样样通,样样松”,但是多涉及一些领域,能开阔自己的眼界,技多不压身嘛,不求面面俱到,只求某一个方向精进,其他方面过得去就行了。 基于这样的原因,虽然我知道这个公众号有很多问题,但是,暂时没有什么更好的办法了。 唯一能做的,还是回到初心,你为什么要做这个公众号,目的是什么? 想明白这个问题,答案也就呼之欲出了。 原因很简单,只是作为鞭策自己的工具,公众号最大的受益者,其实就是我自己。 并不指望能赚钱,发财也轮不到我。 通过输出倒逼输入,就是为了避免自己过于愚昧、无知、浅薄,这是我深恶痛绝、永远无法忍受的。 不求自己多有智慧,至少不显得那么愚蠢,懂得一点点知识,这就够了。 2025 年,写的电影观后感、书籍读后感,是真的花了很多时间,基本上都有五六个小时吧,有时一个下午就这样过去了,都是一字一句,反复斟酌的。 虽然我知道,可以通过 AI 润色,但我觉得,自己写的东西才是有血有肉、真实的。 通常,只会让 AI 帮我检查错别字、标点符号,最多就是分析行文逻辑是否有漏洞,并不奢望文章写得多完美。 AI 优化后的版本,如果太好了,我就会想,我自己能写得出来吗?是我平时写的水平吗? 如果不是,然后把它当成是我自己写的,就感觉很别扭,不劳而获的感觉,让我觉得很有负罪感。 所以,写这两种文章很花时间和精力,但我一直觉得很值得。 以前,我还挺羡慕当演员的,因为可以演不同的角色,意味着能有不一样的人生。 出演什么角色,就要学习角色自身具备的技能,比如演打篮球的,你就得知道怎么打篮球吧,不要求打得多好,至少体验过、学习过,开阔了眼界,新增了一份认知。 就好像一直说的彭于晏为了《破风》苦练自行车获得专业赛车手证书,学习潜水获得潜水员证书,然后是张震,因为参演《一代宗师》,把八极拳练到了全国比赛冠军的专业水平。 但是,后来,我想明白了,其实,如果你要做,不一定非得是什么身份才行,就好像写电影观后感,同样可以达成这种效果。 ...

2025-01-01 · 1 分钟 · 121 字

ARTS Week 45

Algorithm 本周的算法题为 1475. 商品折扣后的最终价格 给你一个数组 prices ,其中 prices[i] 是商店里第 i 件商品的价格。 商店里正在进行促销活动,如果你要买第 i 件商品,那么你可以得到与 prices[j] 相等的折扣,其中 j 是满足 j > i 且 prices[j] <= prices[i] 的 最小下标 ,如果没有满足条件的 j ,你将没有任何折扣。 请你返回一个数组,数组中第 i 个元素是折扣后你购买商品 i 最终需要支付的价格。 示例 1: 输入:prices = [8,4,6,2,3] 输出:[4,2,4,2,3] 解释: 商品 0 的价格为 price[0]=8 ,你将得到 prices[1]=4 的折扣,所以最终价格为 8 - 4 = 4 。 商品 1 的价格为 price[1]=4 ,你将得到 prices[3]=2 的折扣,所以最终价格为 4 - 2 = 2 。 商品 2 的价格为 price[2]=6 ,你将得到 prices[3]=2 的折扣,所以最终价格为 6 - 2 = 4 。 商品 3 和 4 都没有折扣。 实现代码如下: const finalPrices = function (prices) { let priceDifferences = [] prices.forEach((currentPrice, currentIndex) => { let lowerPriceIndexes = [] let priceDifference = 0 prices.forEach((comparePrice, compareIndex) => { if (compareIndex > currentIndex && comparePrice <= currentPrice) { lowerPriceIndexes.push(compareIndex); } }) if (lowerPriceIndexes.length === 0) { priceDifference = currentPrice } else { let minIndex = Math.min(...lowerPriceIndexes) priceDifference = currentPrice - prices[minIndex] } priceDifferences.push(priceDifference) }) return priceDifferences } 解题思路: 先遍历查询满足条件的下标,然后获取最小下标。 如果没有符合条件的折扣,则使用当前价格。 Review Mystery drones in USA causing alarm - Breaking News English Lesson ...

2025-01-01 · 1 分钟 · 175 字

unplugin-vue-router 的基本使用

1. 前言 在Vue3开发过程中,每次创建新的页面都需要注册路由,需要在src/router.ts中新增页面的路径,并将URL路径映射到组件中,如下所示: import { createMemoryHistory, createRouter } from 'vue-router' import HomePageView from './HomePageView.vue' import DevListView from './DevListView.vue' const routes = [ { path: '/', component: HomePageView }, { path: '/DevListView', component: DevListView }, ] const router = createRouter({ history: createMemoryHistory(), routes, }) 虽然看起来工作量不大,但如果页面特别多,就会感到繁琐。即使是微小的改动,如组件名的修改,也需要在路由文件中反复确认,反复如此就会感到不便。为了简化这部分工作,我想到了一个解决方案,那就是开发一个Vue3菜单路由生成工具,只需填写组件的中文名称,根据树形结构生成一个与routes结构相同的数组,然后替换使用即可。但还是觉得不够好,随后想到既然自己能发现这个问题,别人可能早已有了应对方案。接着,我就发现了 unplugin-vue-router 插件。 2. 配置 2.1 安装 npm install -D unplugin-vue-router 2.2 vite.config.ts import VueRouter from 'unplugin-vue-router/vite' export default defineConfig({ plugins: [ VueRouter({ /* options */ }), // ⚠️ Vue must be placed after VueRouter() Vue(), ], }) 2.3 tsconfig.json { "include": [ // other files... "./typed-router.d.ts" ], "compilerOptions": { // ... "moduleResolution": "Bundler", // ... } } 2.4 vite-env.d.ts /// <reference types="unplugin-vue-router/client" /> 3. 应用 3.1 常规方式 因为项目布局是基于Element Plus组件库的<el-container>搭建的,其中对<e-main>进行了处理,<router-view>是 Vue Router 的一个内置组件,用于渲染匹配当前路由的组件,如下所示: ...

2024-12-29 · 2 分钟 · 286 字

组态页面渲染器通过npm包方式使用页面没有渲染成功的问题

前言 在项目开发过程中,计划将组态页面的渲染器集成到组件库,以 npm 包的形式供后续项目模板复用。如此一来,倘若组态页面渲染出现问题,便能简化修复与迭代工作。 遇到问题 采用本地引入方式开发完成后,切换至 npm 包方式使用,此时面包屑可正常显示,然而组态页面部分却呈现空白状态。 分析问题 既然面包屑显示正常,那就表明通过向组件传入 Router 对象进行相关操作并无问题,毕竟面包屑与组态页面共同构成了项目中的具体功能页面。不可能 Router 有问题,面包屑却还能正常显示。 推测组件打包生成 npm 包后,自身未携带 Vue 环境,致使 Vue 内置特殊元素 <component> 渲染失败,此前使用 Pinia 时就碰到过类似情况。 那么,该如何在组件里创建 Vue 环境呢? 要是不使用 <component>,而改用渲染函数来实现会怎样呢? markRaw 的作用是什么? 利用渲染函数实现后,发现打包成 npm 包使用时依旧出现空白,这就说明并非 <component> 的问题,如此一来,问题范围便缩小到组态页面的数据源上了。 对比本地引用和 npm 包两种方式下的数据请求差异,发现使用 npm 包时,存在接口未发起请求的情况,进而定位到问题根源。 问题原因 由于该渲染器是在项目中使用,其请求的 baseURL 是通过 window.location.origin 获取的。在本地引入使用时,本地开发服务器地址为 http://localhost:3000/,并且在 Vue 项目的 vite.config.ts 中已做代理,所以请求正常。但当把渲染器代码移至组件库并以 npm 包方式使用时,它自行获取的请求地址仍是 http://localhost:3000/,并未经过代理处理,致使请求资源的接口异常,最终造成页面空白。 const baseURL = import.meta.env.DEV ? "http://192.168.50.20" : window.location.origin 总结 解决 Bug 的思路仍有待提升,自己还是过于急于求成了。刚察觉到问题的一点苗头,就贸然行事、妄下结论,实在有些武断。一开始认定是 <component> 的问题,就应当采用排除法,不该如此笃定。实际上,只需用简单示例测试一下,便能排除该选项,也不至于耗费大量精力深入研究如何在组件里创建 Vue 环境?如何用渲染函数实现组件?平白浪费了许多时间。 每次解决完一个 Bug 之后都会觉得,这个算啥问题,真的不值一提。可在解决过程中,却常常感到无奈,好几次都差点放弃。原本代码放在项目中使用正常,非要解耦,感觉像是没事找事。不过,最终还是坚持了下来,觉得应该差不多能弄清楚问题所在了。虽说花费的时间不太划算,就如同这篇文章写得好似没什么价值一般,但总结一下解决 Bug 的心路历程,相信还是会有所收获的。积累到一定程度,便能为自己提供更多面对问题时的视角与启发。 ...

2024-12-20 · 1 分钟 · 77 字