16 条 yyds 的代码规范

📅 发布时间:2026/7/5 9:42:34 👁️ 浏览次数:
16 条 yyds 的代码规范
引言代码规范是软件开发中的“交通规则”。没有规矩不成方圆尤其在团队协作中统一的代码规范能大幅降低沟通成本提高代码的可读性和可维护性。良好的代码规范不仅能让代码看起来像一个人写的更能减少 bug 的出现延长项目的生命周期。本文将深入探讨 16 条堪称“永远的神”的代码规范每条规范都会从“是什么”、“为什么”、“怎么做”三个维度展开并结合正反示例和最佳实践。全文预计篇幅约 2 万字希望能帮你建立起一套扎实的代码规范意识。1. 命名要有意义名字本身就是最好的注释1.1 规范描述变量、函数、类、模块的命名必须能够准确表达其用途和含义。名字应该是自解释的让阅读者仅通过名字就能理解代码的意图而不必深入实现细节。1.2 为什么重要可读性有意义的命名能大幅降低代码的阅读难度。试想一下当你看到int d;和int elapsedTimeInDays;哪个更清晰可维护性半年后回头看自己的代码如果命名含糊你可能需要花费大量时间重新理解逻辑。团队协作在团队中每个人都能快速理解他人代码减少沟通成本。1.3 最佳实践使用揭示意图的名字选择能直接回答“为什么存在”、“做什么”、“怎么用”的名字。java// 坏 int d; // 经过的时间单位天 // 好 int elapsedTimeInDays;避免误导不要使用与功能无关的词语。例如不要将一组账号命名为accountList除非它真的是List类型如果是一个Set用accountGroup或accountSet更好。做有意义的区分不要仅仅为了满足语法而添加无意义的数字或废话。比如a1、a2这种序列命名毫无意义。nameString通常不如name因为类型已知getActiveAccount()和getActiveAccounts()才能区分单复数。使用可读的名称不要使用缩写除非是行业通用缩写如 HTML, XML。calcAnn不如calculateAnnualAverage。类名和对象名应该是名词或名词短语如Customer、WikiPage。方法名应该是动词或动词短语如save、delete、getUserInfo。1.4 示例对比python# 坏 x 100 def process(a, b): return a * b - 5 # 好 tax_rate_percent 100 def calculate_net_price(gross_price, discount): return gross_price * discount - tax_rate_percent1.5 扩展思考命名不仅限于英文单词国内团队也可以使用拼音但强烈建议使用英文。可以结合领域驱动设计DDD使用业务领域的术语让代码与业务语言保持一致。2. 遵循一致的命名规范团队需要一部“字典”2.1 规范描述团队必须约定一套统一的命名风格并在整个项目中严格遵守。常见的风格有驼峰式camelCase、帕斯卡式PascalCase、下划线式snake_case、匈牙利命名法等。现代开发主要使用前三者。2.2 为什么重要消除歧义统一的风格让代码看起来像一个人写的避免因风格不同造成的视觉混乱。提高可预测性当看到maxPrice你就能推断出这是一个变量驼峰而MaxPrice可能是一个类名帕斯卡。工具支持统一的命名风格有助于 IDE 进行正确的语法高亮和重构。2.3 最佳实践类名、接口名、枚举名使用大驼峰PascalCase每个单词首字母大写。如UserService、HttpClient。方法名、局部变量名、参数名使用小驼峰camelCase首字母小写后续单词首字母大写。如getUserName、totalCount。常量名使用全大写加下划线UPPER_SNAKE_CASE。如MAX_CONNECTIONS、DEFAULT_TIMEOUT。包名全部小写点号分隔如com.example.project。布尔类型变量通常用is、has、can开头如isDeleted、hasChildren。数据库字段、配置文件键常用小写加下划线snake_case如user_name、created_at。遵守语言社区惯例不同语言有不同习惯例如 Java 遵循驼峰Python 官方建议类名大驼峰变量和方法用小写加下划线。尽量遵循所在语言的 PEP8 或官方规范。2.4 示例对比javascript// 坏混搭风格 var User_name John; function Getuserage() { ... } const MAXSIZE 100; // 好一致风格 var userName John; function getUserAge() { ... } const MAX_SIZE 100;2.5 扩展思考可以使用工具如 Checkstyle、ESLint、Pylint自动检查命名规范并将其集成到 CI 流程中确保每次提交都符合规范。3. 函数要短小只做一件事3.1 规范描述函数应该尽可能短小并且只完成一个功能单一职责原则。如果一个函数超过 20-30 行或者你无法用一句简单的话描述它的作用那么它很可能需要拆分。3.2 为什么重要可读性短小的函数易于理解一眼就能看出其逻辑。可复用性单一职责的函数更容易在其他地方复用。可测试性小函数容易编写单元测试覆盖所有分支。减少错误复杂的函数往往隐藏着更多的 bug拆分后每个小函数逻辑清晰错误概率降低。3.3 最佳实践函数的第一行应该是一个概括性的注释如果需要但最好函数名就能概括。抽象层次一致函数内部的代码应该处于同一个抽象层次。例如一个函数如果负责获取数据就不要在里面混合 UI 渲染。使用提取方法重构如果一段代码可以独立出来并且有一个清晰的名字就应该提取为单独的函数。遵循“缩进层级不超过两层”过深的嵌套往往意味着函数做了太多事。避免 Flag 参数向函数传入布尔值表示执行不同逻辑通常说明函数做了两件事应拆分为两个函数如renderForPrint()和renderForScreen()而不是render(boolean isPrint)。3.4 示例对比java// 坏一个函数做三件事验证、计算、发送邮件 public void processOrder(Order order) { if (order null || order.getItems().isEmpty()) { throw new IllegalArgumentException(Invalid order); } double total 0; for (Item item : order.getItems()) { total item.getPrice() * item.getQuantity(); } order.setTotal(total); emailService.sendOrderConfirmation(order); } // 好拆分为三个函数 public void processOrder(Order order) { validateOrder(order); calculateTotal(order); sendConfirmation(order); } private void validateOrder(Order order) { ... } private void calculateTotal(Order order) { ... } private void sendConfirmation(Order order) { ... }3.5 扩展思考函数长短没有绝对标准但通常认为一个函数不应超过一个屏幕20-30 行。Robert Martin 在《Clean Code》中甚至建议函数不应超过 4-5 行。关键在于职责单一。4. 注释要解释“为什么”而不是“什么”4.1 规范描述注释应该用来解释代码的“为什么”Why和“注意事项”而不是简单重复代码做了什么What。好的代码本身应该是自解释的读者通过阅读代码就能知道“什么”。4.2 为什么重要避免噪音重复代码的注释如i; // i加1毫无价值只会干扰阅读。传递意图复杂的业务逻辑或特殊处理需要注释说明背后的原因帮助后人理解当时的决策。警示标注潜在的陷阱、TODO、FIXME 等。4.3 最佳实践不要用注释代替好代码如果一段代码需要注释才能看懂试着重构它让它更清晰。写注释解释为什么例如为什么使用某种算法为什么需要特殊处理某个边界情况。用注释解释业务规则例如“根据税法第 X 条年收入超过 10 万的部分税率提高至 45%”。保持注释与时俱进修改代码时务必同步更新注释否则过时的注释比没有注释更可怕。使用标准标记如TODO、FIXME、HACK并附上作者和日期。文档型注释对于公共 API使用文档注释如 Javadoc、JSDoc描述方法的功能、参数、返回值、异常等。4.4 示例对比python# 坏 x x 1 # 将 x 加 1 # 好 # 补偿由于浮点运算可能带来的精度损失向上取整 total math.ceil(original_total * 100) / 1004.5 扩展思考有些团队推崇“无注释代码”要求代码完全自解释。但完全无注释有时难以表达高层次的设计意图。平衡点是代码本身无法表达的内容如业务背景、设计权衡才需要注释。5. 代码格式化要统一缩进、空格、括号5.1 规范描述代码的排版风格缩进、空格、换行、括号位置等必须统一通常由格式化工具如 Prettier、Black、clang-format自动处理避免人工争论。5.2 为什么重要消除无意义的争论团队不必为“缩进用 tab 还是空格”这种问题浪费时间。提高可读性一致的格式让代码结构清晰易于浏览。降低合并冲突统一的格式减少了因格式差异导致的代码合并冲突。5.3 最佳实践使用自动化工具配置好格式化工具并集成到编辑器中保存时自动格式化或者在提交前钩子pre-commit hook中强制执行。常见的格式化规则缩进通常为 2 或 4 个空格禁止混用 tab 和空格。行宽通常限制为 80-120 字符超出换行。括号风格KR 风格左括号在行末或 Allman 风格左括号新起一行选择一个统一。空格操作符两侧加空格逗号后加空格关键字后加空格如if (condition)。团队约定配置文件将格式化工具的配置文件如.prettierrc、.editorconfig纳入版本控制确保所有成员使用相同配置。代码审查中不讨论格式格式问题应由工具解决审查应专注于逻辑和设计。5.4 示例对比java// 坏不一致的格式 public void test(){ int a1; if(a0){ System.out.println(a); } } // 好统一格式 public void test() { int a 1; if (a 0) { System.out.println(a); } }5.5 扩展思考除了基本格式还应统一 import/using 的顺序、文件末尾换行、尾随逗号等细节。IDE 如 IntelliJ IDEA、VS Code 都可以配置自动格式化。6. 避免魔法数字和字符串使用常量6.1 规范描述代码中直接出现的字面量数字、字符串称为“魔法数字/字符串”。应该将它们定义为有意义的常量final static或const并集中管理。6.2 为什么重要可读性if (status 2)是什么意思不如if (status STATUS_ACTIVE)一目了然。可维护性如果这个数字在多个地方使用修改时只需要改一处避免遗漏。减少错误手写魔法数字容易输错常量定义可以避免。6.3 最佳实践定义常量使用private static final int MAX_RETRY_COUNT 3;或const MAX_RETRY_COUNT 3;。枚举代替整型常量对于相关的常量集合可以使用枚举如Status.ACTIVE。配置文件对于可能随环境变化的常量如数据库连接、超时时间应该放在配置文件如 properties、yaml中而不是代码常量。常量命名全大写加下划线。6.4 示例对比javascript// 坏 setTimeout(() { ... }, 3000); // 好 const DEFAULT_TIMEOUT_MS 3000; setTimeout(() { ... }, DEFAULT_TIMEOUT_MS); // 更好从配置文件读取 const config require(config); const timeout config.get(network.timeout);6.5 扩展思考某些情况下魔法数字在特定上下文中是显而易见的比如循环中for (int i 0; i 10; i)的 10 如果代表明确含义比如数组长度最好用array.length代替。如果 10 是业务上的固定数量应该定义常量。7. 处理错误和异常不要忽略7.1 规范描述代码必须正确处理可能出现的错误和异常绝不能静默忽略比如空的 catch 块也不该用错误码代替异常除非在性能极端敏感的场景。7.2 为什么重要可靠性忽略异常会让程序在错误状态下继续运行导致更严重的问题。可调试性正确的错误处理能记录有用信息帮助定位问题。用户体验程序可以优雅地降级或提示用户而不是直接崩溃。7.3 最佳实践使用异常机制在支持异常的语言中优先使用异常而不是返回错误码。不要捕获异常却不处理空的 catch 块是万恶之源。至少应该记录日志。抛出具体的异常类型避免直接throw new Exception()应该抛出有意义的子类如IllegalArgumentException、BusinessException。在合适的层次处理异常底层捕获异常后可以包装成业务异常再向上抛或者统一由上层处理。使用 finally 释放资源确保文件流、数据库连接等资源被正确关闭或使用 try-with-resources。记录上下文信息捕获异常时记录关键参数方便复现。7.4 示例对比java// 坏空的 catch忽略异常 try { connection dataSource.getConnection(); } catch (SQLException e) { // 什么都没做程序继续运行但 connection 为 null } // 好记录日志抛出自定义异常 try { connection dataSource.getConnection(); } catch (SQLException e) { log.error(Failed to get database connection for user {}, userId, e); throw new DataAccessException(Unable to connect to database, e); }7.5 扩展思考对于预期可能发生的错误如用户输入错误可以使用返回结果对象如Either、Optional来避免异常控制流。但异常仍然是处理不可预知错误的主流方式。8. 编写单元测试测试边界条件8.1 规范描述代码必须配有单元测试测试不仅要覆盖正常路径还要覆盖边界条件和异常情况。8.2 为什么重要保证质量测试可以捕获回归错误确保修改不会破坏现有功能。文档作用测试用例展示了代码的预期行为是活的文档。设计驱动写测试能促使你写出可测试的代码解耦、单一职责。信心有了完善的测试重构时更有信心。8.3 最佳实践测试命名清晰使用“测试方法_场景_预期结果”的格式如testDivide_DivisorIsZero_ThrowsException。遵循 AAA 模式Arrange准备数据、Act执行操作、Assert断言结果。覆盖边界值比如测试一个方法接收 0、负数、最大值、空集合等。使用 Mock 隔离依赖对外部服务、数据库等进行模拟确保单元测试的快速和独立。测试覆盖率不是唯一指标追求高覆盖率但更重要的是测试的质量和关键逻辑的覆盖。8.4 示例对比python# 函数 def divide(a, b): return a / b # 测试坏只测了正常情况 def test_divide(): assert divide(10, 2) 5 # 测试好包含了边界和异常 import pytest def test_divide_normal(): assert divide(10, 2) 5 def test_divide_by_zero(): with pytest.raises(ZeroDivisionError): divide(10, 0) def test_divide_negative(): assert divide(-10, 2) -58.5 扩展思考测试金字塔底层单元测试最多中间层集成测试顶层端到端测试最少。优先编写单元测试确保核心逻辑正确。9. 不要重复自己DRY 原则9.1 规范描述DRYDon‘t Repeat Yourself原则要求避免代码重复任何重复的逻辑都应该被提取出来复用同一份实现。9.2 为什么重要减少维护成本如果相同的逻辑出现在多处修改时需要全部找到并修改极易遗漏。提高一致性复用保证所有地方的行为一致。代码精简重复代码使代码库臃肿难以阅读。9.3 最佳实践提取函数重复的代码块可以提取成公共函数。提取基类/工具类多个类共有的方法可以放到父类或工具类中。使用设计模式如模板方法模式将不变的部分放在基类可变的部分由子类实现。注意不要过度抽象如果两段代码只是偶然相似但变化方向不同强行 DRY 反而会增加耦合。不仅仅是代码DRY 也适用于文档、配置等。9.4 示例对比java// 坏重复的验证逻辑 public void saveUser(User user) { if (user.getName() null || user.getName().isEmpty()) { throw new ValidationException(Name is required); } // ... 保存 } public void updateUser(User user) { if (user.getName() null || user.getName().isEmpty()) { throw new ValidationException(Name is required); } // ... 更新 } // 好提取验证方法 private void validateUserName(User user) { if (user.getName() null || user.getName().isEmpty()) { throw new ValidationException(Name is required); } } public void saveUser(User user) { validateUserName(user); // ... } public void updateUser(User user) { validateUserName(user); // ... }9.5 扩展思考有时候重复是必要的比如两个微服务之间不能共享代码库那么各自保留一份实现是可以接受的但应确保逻辑同步。DRY 主要适用于同一模块或同一代码库内。10. 保持代码简单避免过度设计KISS10.1 规范描述KISSKeep It Simple, Stupid原则要求代码尽量简单直接避免过度工程化和引入不必要的复杂性。10.2 为什么重要易于理解简单的代码容易上手新人也能快速参与。降低错误率复杂的代码隐藏更多 bug且难以调试。提高开发效率设计简单开发快维护易。10.3 最佳实践YAGNIYou Ain‘t Gonna Need It不要为未来可能需要的功能提前设计除非当前确实需要。选择最简单的解决方案能用 if-else 解决的不要引入设计模式能用数组的不要用红黑树。避免过度封装不要为了“优雅”而创建过多的抽象层导致调用链过长。代码自解释变量名、函数名清晰减少对注释的依赖。重构时保持简单每次重构只做必要的改进不要试图一次性重构成完美架构。10.4 示例对比java// 坏过度设计用策略模式处理一个简单的条件 public interface GreetingStrategy { String greet(String name); } public class EnglishGreeting implements GreetingStrategy { public String greet(String name) { return Hello name; } } public class SpanishGreeting implements GreetingStrategy { public String greet(String name) { return Hola name; } } // ... 还需要工厂类 // 好简单 if public String greet(String name, String language) { if (en.equals(language)) return Hello name; if (es.equals(language)) return Hola name; return name; }10.5 扩展思考简单不是简陋。在满足需求、保证可维护性的前提下选择最直接的实现方式。随着需求变化可以渐进式地重构。11. 使用版本控制提交信息清晰11.1 规范描述所有代码必须使用版本控制系统如 Git并且每次提交的提交信息应该清晰、规范描述本次修改的内容和原因。11.2 为什么重要历史追溯清晰的提交信息帮助开发者了解某行代码为什么被修改是排查问题的关键。团队协作方便 Review也便于生成变更日志。回滚依据知道每个提交的范围可以精确回滚特定功能。11.3 最佳实践遵循提交信息规范如 Conventional Commits 规范格式为type(scope): subject例如feat(user): add login API。feat: 新功能fix: 修复 bugdocs: 文档更新style: 代码格式调整refactor: 重构test: 测试相关chore: 构建/工具链变动主题行不超过 50 字符使用祈使句。正文详细说明如果必要可以在提交信息中说明修改的原因、影响范围、相关 issue 等。一次提交只做一件事避免将多个无关的修改混在一个提交中。提交前审查变更使用git diff确认要提交的内容正确。11.4 示例对比text坏 fix bug 好 fix(auth): resolve NullPointerException when token is missing When the Authorization header is absent, the previous code attempted to parse a null string, causing NPE. Now we check for null and return 401 immediately. Fixes #12311.5 扩展思考可以结合 Git Hook如 commit-msg或 CI 工具自动检查提交信息是否符合规范。12. 代码审查互相学习12.1 规范描述所有代码在合并到主分支之前必须经过至少一名同事的审查Code Review。审查不是找茬而是互相学习、共同提高的过程。12.2 为什么重要发现缺陷审查可以发现逻辑错误、潜在 bug 和不符合规范的地方。知识共享通过审查团队成员能了解彼此的代码避免知识孤岛。提高代码质量知道自己写的代码会被审查开发者会自觉写得更好。统一风格审查有助于维护团队规范的一致性。12.3 最佳实践小批量提交一次审查的代码量不宜过大通常 200-400 行否则难以深入。审查态度积极评论应针对代码而不是人提出建议时用“也许可以...”而不是“你应该...”。关注重点设计合理性逻辑正确性边界条件处理命名和注释测试覆盖作者有义务回应对每条评论做出回应修改或解释。自动化先行让工具检查格式、静态分析等审查人员专注于逻辑和设计。12.4 示例对比text评论者这个函数命名是 getData但它实际上做了数据库查询和缓存更新是不是可以拆分成两个更明确的函数 作者同意已拆分为 fetchFromDb 和 updateCache。12.5 扩展思考代码审查不仅限于结对编程前的交叉审查也可以利用工具如 GitHub Pull Requests、GitLab Merge Requests进行异步审查。13. 避免深层嵌套尽早返回13.1 规范描述代码中的条件嵌套if 套 if应该尽量减少通过提前返回Guard Clauses来降低嵌套深度。13.2 为什么重要可读性深层嵌套让代码难以跟随逻辑流向大脑需要记住多层条件。降低复杂度减少嵌套意味着减少循环复杂度和出错可能性。突出正常路径提前返回可以让正常流程代码保持平坦不被错误处理干扰。13.3 最佳实践使用卫语句对于前置条件检查如果不满足则直接返回或抛出异常。java// 坏 if (user ! null) { if (user.isActive()) { // 几十行逻辑 } } // 好 if (user null) return; if (!user.isActive()) return; // 几十行逻辑将复杂条件封装成方法例如if (user ! null user.isActive() user.hasPermission(WRITE))可以封装成user.canWrite()。避免在循环中使用深层条件考虑将循环内的复杂逻辑提取成函数。使用多态代替条件判断对于 if-else 分支很多且类型相关的逻辑考虑用多态替换。13.4 示例对比javascript// 坏三层嵌套 function processOrder(order) { if (order) { if (order.paid) { if (order.items.length 0) { // 处理订单 } else { console.log(No items); } } else { console.log(Not paid); } } else { console.log(No order); } } // 好提前返回 function processOrder(order) { if (!order) { console.log(No order); return; } if (!order.paid) { console.log(Not paid); return; } if (order.items.length 0) { console.log(No items); return; } // 处理订单 }13.5 扩展思考深层嵌套往往是函数职责不单一的信号。如果提前返回后代码依然很长考虑将主干逻辑提取成独立的函数。14. 使用设计模式恰当不要滥用14.1 规范描述设计模式是解决常见问题的成熟方案但不应为了用模式而用模式。应当根据实际场景选择最适合的模式避免过度设计。14.2 为什么重要模式有其适用范围用错模式会引入不必要的复杂度。模式增加抽象过多的模式会让代码晦涩难懂破坏简单性原则。模式不是银弹有时简单的代码比套用模式更清晰。14.3 最佳实践理解模式的意图在应用某个模式前先理解它解决了什么问题你的场景是否真的需要。优先考虑简单方案如果简单的 if-else 或继承就能满足需求就不要引入策略模式或状态模式。代码中出现“臭味”时再重构当代码出现重复、臃肿的 switch 等问题时考虑用模式重构。团队共识如果团队对某个模式不熟悉使用前应沟通清楚。学习模式的变体不要生搬硬套可以根据语言特性调整实现。14.4 示例对比java// 坏为了用工厂模式而建工厂 public class UserFactory { public User createUser(String type) { if (admin.equals(type)) return new AdminUser(); if (normal.equals(type)) return new NormalUser(); return null; } } // 如果只有两种用户且创建逻辑简单直接 new 就行不需要工厂。 // 好当创建逻辑复杂或需要解耦时再用工厂 // 假设 User 的子类很多且创建需要复杂配置14.5 扩展思考学习设计模式时重点在于理解其思想组合优于继承、开闭原则等而不是死记硬背类图。在重构中自然引入模式而非预先设计。15. 关注性能但不要过早优化15.1 规范描述编写代码时应考虑性能但不要进行无根据的“过早优化”。应该先保证代码清晰正确然后通过 profiling 找出真正的瓶颈再优化。15.2 为什么重要过早优化浪费时间你可能会优化一段代码而它根本不是性能瓶颈。增加复杂度优化往往使代码更难懂、更难维护。80/20 法则20% 的代码消耗了 80% 的资源找到那 20% 才是关键。15.3 最佳实践编写清晰的代码通常情况下简单的代码已经足够高效。遵循常识例如循环内不要重复查询数据库避免 O(N^2) 算法。使用性能分析工具如 JProfiler、VisualVM、Chrome DevTools找出热点。优化热点针对分析结果进行优化如缓存、算法改进、异步处理。衡量优化效果优化前后进行对比确保确实有提升。15.4 示例对比java// 过早优化用 StringBuilder 拼接常量字符串 String sql new StringBuilder(SELECT * FROM users WHERE id ).append(id).toString(); // 实际上编译器会优化直接写 SELECT ... id 更清晰。 // 真正需要优化时 // 坏循环内查询数据库 for (Order order : orders) { User user userDao.findById(order.getUserId()); // 每次查询 } // 好批量查询 ListLong userIds orders.stream().map(Order::getUserId).collect(Collectors.toList()); MapLong, User userMap userDao.findByIds(userIds);15.5 扩展思考性能优化是一个迭代过程。先保证正确性再考虑优化。同时要注意可读性和性能的平衡。16. 安全性考虑输入验证防止注入16.1 规范描述代码必须考虑安全性对所有外部输入用户输入、HTTP 参数、文件上传等进行验证和清理防止常见的安全漏洞如 SQL 注入、XSS、CSRF。16.2 为什么重要保护数据安全漏洞可能导致数据泄露、篡改或丢失。法律责任安全问题可能带来法律风险和品牌损害。用户信任安全是产品的基石。16.3 最佳实践永远不要信任用户输入所有输入都必须经过验证类型、长度、格式、范围。使用参数化查询或 ORM防止 SQL 注入不要拼接 SQL 字符串。输出编码在 HTML 中输出用户内容时进行 HTML 编码防止 XSS。使用安全框架如 Spring Security、Shiro处理认证和授权。密码存储使用加盐哈希如 bcrypt存储密码不要明文存储。最小权限原则程序运行只赋予必要的最小权限。依赖项安全定期更新依赖库修复已知漏洞。16.4 示例对比java// 坏拼接 SQL存在注入风险 String sql SELECT * FROM users WHERE username username ; // 好使用 PreparedStatement PreparedStatement stmt connection.prepareStatement(SELECT * FROM users WHERE username ?); stmt.setString(1, username); // XSS 防范前端或后端对输出编码 // 在 JSP 中使用 c:out value${userInput} /