@lsmn
2018-09-06T11:04:09.000000Z
字数 10509
阅读 1837
持续交付
TDD
BDD
CD
持续交付是一种保证系统在整个开发过程中都处于可发布状态的工作方式。本文介绍了Siemens Healthineers的一个大型软件开发组织如何开始向持续交付转型,描述了他们在规范化的医疗领域逐步、安全地改变开发过程所采用的策略与技巧。
本文要点
- 持续交付是一种保证系统在整个开发过程中都处于可发布状态的工作方式。这通常需要在组织、技术和开发文化方面的变革才能实现。如果你现在无法发布到生产环境,那么就不是持续交付。
- 在通往持续交付的旅途中有许多阶段性的成果。那是个逐步采用的过程,而不是单次的开关切换。
- 采用持续交付需要项目中的所有角色都有着共同的目标。我们一起工作,快速学习,向用户交付优秀的软件。
- 在任何组织里,不同的团队会以不同的速率采用持续交付的不同部分。转型过程不是一体通用的。
- 持续交付需要我们弄清楚基本原则。我们践行诸如可靠的自动化测试这样的原则,确定部署管道的恰当范围以及需求的有效分解。
持续交付是一种保证系统在整个开发过程中都处于可发布状态的工作方式。这个看似简单的理念需要我们重新思考传统软件开发过程的所有方面。
在这篇文章中,我们将介绍Siemens Healthineers的一个大型软件开发组织如何开始向持续交付转型,描述了他们在规范化的医疗领域逐步、安全地改变开发过程所采用的策略与技巧。
teamplay由Siemens Healthineers开发。这是一个面向医疗机构的绩效管理解决方案,汇集医疗健康专业人士,通过团队努力推动医疗和人类健康。
通过连接医疗机构和它们的影像装置,teamplay应用旨在建立世界上最大的放射学和心脏病学团队,为其成员提供工具,处理大数据,应对这些部门中日益增加的成本压力。
基于云的解决方案teamplay及其应用将通过提供明白易懂的绩效数据概览帮助做出及时明智的决策。
它监控数量,如影响吞吐量或剂量水平、员工利用率、整个部门的房间和资源,一直到每种设备和过程,简化报表,显示工作流何处需要调整。
它会连接teamplay的其他用户以及他们的数据,提供比较基准,与其他医疗保健提供商毫不费力地交换影像和报告。
teamplay Usage和teamplay Dose是teamplay网络托管的两个应用程序的例子。teamplay Usage
使得医疗机构可以监控分值和趋势,如病人变化次数或者不同影像扫描设备执行检查的次数。
[点击查看大图]
teamplay Dose使卫生机构可以获知扫描设备使用的辐射剂量水平,突出内部阈值和国家阈值的偏差。
该网络包含2550多个teamplay连接,而且数量还在增加。由第三方接入teamplay公共API的应用程序数量也在稳步增长。一旦医疗健康机构连接到这个网络,该机构的影像扫描设备就会不断地向云端发送数据。
这些数据被在微软Azure云上运行的teamplay消费。用户通过一个用户界面同这些服务进行交互,该界面是用AngularJS实现的一个单页应用程序。前端集成很简单,通过iFrames,因此,使用teamplay公共API的第三方应用程序可以选择他们自己的技术。
目前,teamplay系统的变更每季度发布一次。我们的目标是大幅提速。这会使得我们可以更快地发布特性,从而可以更快地响应用户需求。从技术的角度讲,更快的交付和更短的循环周期应该可以帮助我们减少批次大小,使得变更更小,风险更低,缩短目前在每次发布前所需的稳定期。通过提升整个过程的质量,我们意在改善发布日期的可预测性。
以此为目标,teamplay开发团队开始致力于部署自动化。首先,我们致力于实现自动化开发部署。然后,我们增加了自动化完整性测试,检查应用程序的主要功能。这些测试可以确定部署是否处于可用状态。它们在部署之后立即执行,并告知开发团队构建是否适用于探索性测试。
部署及完整性测试的自动化给产品稳定性带来了显著的提升。不过,更频繁地发布以及缩短每次发布之前的稳定期仍然是一项挑战。
持续交付(CD)是一种软件开发方法,基于一种更为严格的工程方法。它不仅仅是部署自动化和自动测试技术。CD的目标是使软件在整个开发过程中都处于可发布状态。这个简单的理念影响着组织软件开发方法的每个方面。不管是从技术角度,还是从业务角度,可发布性都是需要的。为此,技术工程和需求工程需要同时做出调整。
我们邀请Dave Farley在我们的软件开发方法的各个方面为teamplay提供帮助。我们的基本目标是提升安全性、稳定性和开发过程的效率。
由于teamplay开发的软件在医疗健康领域使用,要符合领域规范,所以安全性至关重要。合规与高质量产品的生产都极其重要。
teamplay相当传统的开发方法有一些传统的问题。最大的问题也许是,随着软件及组织复杂性的提升,团队以期望的速率开发新特性的能力越来越低。
我们的目标是通过运用良好的工程原则来克服这些困难。我们希望可以提升质量和反馈频率,改善整个组织的合作,使开发文化转向更规范的高质量工程方法。
可以提供高质量反馈的高效过程和机制是使团队和个人能够更好地看到自己的工作和决策成果的基础。良好的反馈是任何高质量过程都具备的特点。
有效的协作至关重要。我们的目标是通过优化实现更为分散的、以任务为中心的领导风格。teamplay组织的分布式特性意味着在靠近工作的地方进行本地决策更为重要。创建高效的团队结构和良好的工作实践可以促成更加分散的决策方法。这很重要,可以推动我们迈向更强大的工程文化。
我们通过培训、辅导以及文化变革方面更广泛的思考推进这些变革。每个团队都接受一些有关持续交付理念和哲学的培训。这有助于人们理解为实现这种变革而对每个人提出的要求。
接下来,我们围绕着我们要求人们采用的一些具体技术进行更细致的实习培训。我们让有CD经验的人和正在学习这种新方法的团队一起工作。
在技术端,我们致力于通过更好的自动化测试和更高效的部署管道提升反馈。故事地图为需求过程提供帮助,类似领域专属语言这种技术的使用可以与故事地图生成的故事联系起来,提供一种以这些“可执行规范”为基础的联合开发方法。
其中许多变革还在进行过程中。采用情况因团队而异,有些在技术上更困难,还有广泛传播的文化,变革尚未完成。不过,这是意料之中的。我们的目标是通过长期的变革过程安全地逐步提升开发过程。
考虑到我们从teamplay开发过程开始持续交付转型,显然,需要缩短团队的测试反馈周期。我们需要24到48个小时才能知道测试执行结果。我们希望通过一系列旨在提升反馈质量的步骤把这个时间缩短到1小时。
为此,需要做一个基本决策。我们可以继续使用现有的管道,一口气部署和评估teamplay,并致力提升其速度。这会涉及扩展我们的构建服务器,既包括横向,也包括纵向。我们需要探索增量编译,仅执行受软件变更影响的测试。另外,我们可以把我们的架构分解成微服务,为每个微服务创建一个专用的管道。
我们决定采用一种折中方案,介于这两个正交选项之间。因此,我们决定为每个teamplay应用程序创建一个专用的的管道。
这种方法不需要我们为了整体部署以及提升随之而来的测试运行速度而投资复杂的工程,也不需要我们把架构分解成非常小的组成部分,并在它们之间设计向后兼容的契约。我们将来可能会考虑微服务方法,但是现在,我们仅致力于把teamplay的每个应用程序作为独立于其他部分的整体来发布。
在决定为每个teamplay应用程序创建一个专有的管道后,我们需要详细讨论下团队如何创建新特性以及新特性如何进入管道。换句话说,我们需要看下团队如何定义、实现和测试特性。
必须要有一个过程,可以确保团队创建的管道输入符合必要的质量要求,可以实现充分、可靠的自动化。仅仅有一个管道,或者每个应用程序一个管道,这并不是目标本身。目标是能够把高质量的应用程序发布到生产环境。要实现这个目标,需要团队为管道提供高质量的输入,一个高效的管道可以尽可能快地运行输入,提供反馈,帮助团队高质量地完成工作。
在一系列的研讨会、咨询会议和培训课程之后,我们创建了自己的持续交付模型,如下图所示:
上图展示了我们希望采用的产品变更定义、测试、实现和部署方式。
这幅图的最上面列出了我们使用的方法:测试驱动开发(TDD)、行为驱动开发(BDD)、领域驱动开发(DDD)、结对编程、结对轮转(两人一组)及团队部署管道。供所有团队成员使用的所有这些方法将使我们达到应用程序总是处于可部署状态的目标。后续,发布决策不会因为达到技术上的就绪状态所需的时间而受阻,而是根据纯业务和管理的考虑来进行。
图中的彩色小人图标代表应该执行特定过程步骤的角色:产品经理(PO)、业务分析师(BA)、设计人员(Des)、架构师(AR)、开发人员(Dev)、运维工程师(Ops)。图中主线的颜色代表相应角色应该使用的方法(TDD、BDD、DDD、结对),图片右上方有图例说明。
还是在图片上方,显示了我们希望使用用户故事地图和BDD场景来详细描述产品的方式。在图片右侧,说明了我们希望使用的产品测试方式,创建一种领域专属语言(DSL)来表示测试用例、验收测试和TDD。在图片底部,展示了我们使用4阶(接受1、接受2、过渡、生产)段团队管道部署产品的方式。
这个图形一直非常有用。除了帮助我们整理思路外,项目成员还可以借助这张图快速找出,按照他们的角色他们应该做什么才能促成持续交付。
让我们看下,如何像上图那样,使一个特性走过整个流程。
假如团队中有人有一个关于新特性的想法。第一步是陈述我们希望测试的假设。这可以通过以下三元组来完成:
过程的所有后续部署都是为了证明上面陈述的假设是对还是错。陈述假设很重要,因为它提供了工作范围:我们只需要实现测试假设所需要的功能和产品检测。
该过程的下个步骤是把想法变成原型,以便从客户那里获得早期反馈,看看这个想法是否引起了他们的共鸣,是否应该进一步投入。这一步是由产品经理和设计人员完成,提供一个低保真原型(例如,手动绘制的纸上原型或概念线框)。
接下来,产品经理和设计人员会大致地介绍特性,只是为了向项目中的其他角色解释。
在下一个步骤中,每个人都参与用户故事地图的创建。用户故事地图以层次化的方式展示与特性关联的用户旅程。产品经理、架构师、业务分析师、设计人员和开发人员全部参与讨论,了解和规划用户在使用该特性时的旅途。重要的是要考虑用户经历的所有步骤,也包括在产品之外执行的那些,如果有的话。团队中的每个人都需要了解用户旅途。
创建完用户故事地图后,下一步是排定工作优先级,确定将会首先发布的一个小故事集。这个故事集将构成我们的最小可行产品(MVP)。然后,从MVP集合中选出一个故事进行改进。
改进使用所谓的BDD场景来完成。每个场景使用Gherkin语言的Given/When/Then语句进行表示。重要的是确保产品经理、架构师、业务分析师、开发人员和运维人员全都可以为故事的场景集合做出贡献,因为每个角色都是从不同的角度来看待项目。
通常,产品经理会从功能的角度贡献最初的场景集合。整个团队一起扩展最初的功能场景列表。举例来说,架构师可以从负载和安全的角度贡献额外的场景。运维工程师可以贡献与监控和日志相关的场景。业务分析师负责确保团队涉及了所有方面(或“某某性”)。
我们旨在保证故事较小。如果用户故事的场景数量超过6个,那么该用户故事就太大,需要在用户故事地图中划分成更小的特性。
我们还希望保证故事专注于用户需求,故事和场景应该不包含任何技术细节(不提及实现细节或UI),而是完全从用户的角度编写。
一个很好的检验是,当客户打进电话要求回答有关特性实现的问题时,1线支持可以使用用户故事和场景。如果用户故事和场景是从用户角度编写的,没有涉及实现细节,那么它们在1级支持回答客户问题时应该会非常有用。
一旦团队就用户故事的BDD场景集合达成一致,就可以逐个测试和实现场景了。我们采用了真正的测试优先方法,在准备好恰当的测试工具之前,不开发任何生产代码。这样可以确保把测试从产品代码分离出来,有一个良好的关注点隔离,这反过来有助于降低测试维护成本。
场景测试的第一步是提出一种新的或者扩展现有的领域专属语言(DSL),在这里是Test DSL。Test DSL的灵感来自领域驱动设计(DDD)方法,从用户故事和场景定义的思路探讨到实际代码,在整个过程中发生的所有对话中使用相同的语言。
借助Test DSL,我们使用Gherkin语言以可执行、可重用的形式定义BDD场景。不过,只有前面定义的场景不包含实现细节时才可能重用。这样做还有一个好处,Test DSL可以由非技术人员编写,他们对问题领域有很好的理解。这使得他们可以准确地说明他们的需求,因为规格说明书是可执行的。
在定义好场景的Test DSL后,就可以由开发人员在验收测试中实现。这是我们第一次做实现决策。我们希望尽可能地通过API进行测试,因为这些测试运行速度快,而且比UI测试要健壮许多。不过,在有些情况下仍然希望通过UI测试,以确保特定的功能在合并前端和后端后仍然有效。
这里还有一点需要说明,就是我们的验收测试用例在DSL级别进行了有效的定义,而且不包含实现细节。这就是说,当我们后续调整验收测试实现时,我们的验收测试用例不需要修改。根据之前使用Test DSL定义的测试用例,我们可以很轻松地验证调整后的产品功能和调整后的验收测试用例实现。
DSL还有一个额外的好处,就是可以跨场景重用场景的部分实现。例如,如果DSL为场景的“When”子句定义了一种系统引导方法,同样的实现通过不同的参数化就可以在其他场景中重用。这有助于提高测试代码重用,因此可以提升测试实现速度。
同时,这有助于验收测试的功能隔离。这点很重要,因为功能隔离测试可以并行执行,互不干扰。这种并行有助于缩短测试反馈周期。
最后,可以在DSL中纳入功能别名,那样,当创建领域对象如Imaging Scanner时,就可以给它们附加UID,确保功能隔离,这样就不必在测试代码中显式指定UID。
Dave建议在构造验收测试基础设施时采用4层架构。这可以实现上文描述的抽象分层、关注点隔离。
如果没有这种隔离,当要测试的系统发生变化时,上层功能测试就会变得脆弱。
这个4层模型是:
使用抽象DSL编写测试用例(第一层)获取用户意图,而不是交互的技术细节,这些“可执行的规格说明书”仍然与要测试的系统保持非常松散的耦合关系。
第二层实现了DSL。共享许多测试用例通用的领域级概念,如AddScannerToHospital、 CalculateRadiationDoseByScan、DistributeScanProtocol。
第三层是一个“协议适配器”的集合。这些适配器把领域(DSL)概念转换成与第四层——要测试的系统——的真实交互。
这使得测试用例编写者可以完全专注于系统需要做“什么”,而不是系统“如何”做。
从这里你可以看到这种方法更详细的介绍:持续交付验收测试——Dave Farley。
在编写好验收测试后,我们预料到它会失败!它在执行时变红了,因为产品代码还没有编写。我们是有意这样做的。我们希望采用“红-->绿-->重构-->提交”的方式。首先实现测试。一旦执行,它就会因为缺少产品代码而变红。
为了使测试通过,我们对产品代码做足够的修改。一旦通过,它就绿了!在把变更提交到管道之前的最后一步是重构,把整个过程梳理一下。
然后,我们提交变更。“红-->绿-->重构-->提交”是有好处的,因为它可以确保在测试代码和产品代码之间有一个很好的抽象。
我们仅为已经存在的测试编写产品代码,测试可以告诉我们产品具体要做什么。由于测试代码是优先编写的,所以它生来就是与产品代码松耦合的。
这样,我们就可以稍后更改产品代码,而不必更改大量的测试代码。这样一来,开发人员就可以快速实现新的用户故事。在“红-->绿-->重构-->提交”工作方式中,不断重构是这个过程不可或缺的部分。我们鼓励开发人员在他们的工作中关注每一次提交的质量。
在编写完验收测试并变红后,我们开始创建代码,满足规格说明书。我们使用TDD编写代码,因此,代码在两个不同的层面进行测试。对于我们而言,这意味着我们需要首先编写单元测试。再次经历红-绿-重构!这可以指导产品代码设计,让我们可以专注于一个小功能。然后,我们根据单元测试告诉我们的东西编写产品代码。一旦新的单元测试通过,我们就稍微重构一下,把单元测试和产品代码段一起提交到团队管道(“红-->绿-->重构-->提交”)。
所有涉及编程的任务,如DSL、验收测试、TDD、产品代码实现、管道,都是由开发人员结对完成的。
两人的工作依据“驾驶员-领航员”的概念开展。驾驶员积极输入代码,考虑所使用的编程语言的结构。领航员指导驾驶员该做什么,他更多的是思考概念层面的东西。
两个人会定期交换驾驶员-领航员角色。而且,一旦一个用户故事的工作完成了,这对开发人员通常就会解散,加入团队中的其他配对。这个过程保证团队的代码所有权,团队快速响应测试失败的能力(因为许多人都可以修复测试),团队适应摩擦而又不对生产力带来较大影响的能力以及团队快速高效地引入新成员的能力。
结对编程看上去是一种昂贵的方法,实际上它不是。它使团队可以更快地产出高质量的成果[1][2][3]。
不过,即使没有这些优势,结对编程帮助团队采用新方法、过程和技术的价值也是非常之大。
结对编程是一种非常强大工具,使团队可以更快地学习,建立和强化团队文化,使开发团队可以在团队内部严格执行必要的原则,提升质量,培育创新。
回到团队管道:一旦提交到达管道,它会经历5个阶段:构建代理、接受1、接受2、过渡和生产。
在构建代理阶段,我们运行在这个过程的TDD阶段创建的所有单元测试,并运用针对项目定义的静态代码分析规则。我们的目标是大约5分钟的时间里从这个阶段的管道获得反馈。5分钟的周期很重要,因为这大概是一对开发人员可以稍事休息而不开始其他工作的时间段。如果反馈在5分钟之内到来,那么这对开发人员得保证精神集中于此次提交,以便可以立即修复失败而不产生任务切换成本。任何单元测试失败都会导致这个阶段的管道变红。变更被拒绝!
如果所有单元测试都通过了构建代理阶段,我们就可以把特定团队开发的这个产品部分部署到团队管道的下一阶段,即接受1。一旦部署,我们就为那个团队运行所有的验收测试。任何验收测试失败会再次导致变更被拒绝。这个阶段的管道会变红。
如果所有验收测试都通过了接受1环境,我们就知道,团队正在开发的这个产品部分可以独立运行。为了确保产品的这个部分在依赖于其他团队时仍然可以运行,我们引入另一个环境,即接受2。在接受2环境里,我们部署来自接受1环境的产品部分及其对其他团队的依赖。一旦部署,我们就在接受2环境里运行一些验收测试,实际测试集成点——契约集成测试。
我们期望,随着团队之间的契约变得日益稳固,我们将来可能可以去掉接受2环境。
我们希望接受1&接受2的反馈周期为大约1小时。这很重要,因为这使得这对开发人员每天可以执行多次提交,也就是说,如果在接受1或接受2环境里出现了失败,开发人员在这一天中还有多次机会来修复问题。修复完成后就可以重新提交变更,看着它一路绿灯通过接受2环境。
这个比较短的反馈周期使得结对的开发人员可以快速推进工作,快速失败,使团队获得很高的速度。
关于1个小时的反馈周期,还有一点很重要,它鼓励结对开发人员在测试失败修复方面的良好行为,回到管道开始的地方,做一次新的提交,而不是修补环境,如接受1和接受2。
如果反馈周期比1小时长很多,比如6小时,开发人员就没有选择了,只能修补环境使测试通过,因为这比等着新的提交从管道起点到进入接受2环境要快(很可能要到下一个工作日了)。
持续交付是一种确保软件总是处于可发布状态的工作方式。
变更反馈传递给开发团队的速度和团队保持软件运行的能力之间存在着密切的联系。
在持续交付中,任何测试失败,我们就会拒绝变更。这就是说,任何测试失败都意味着我们的软件没有处于“可发布状态”。如果我们希望执行持续发布,我们就需要缩短反馈周期!
努力建立短反馈周期是一种推动团队健康变革的极其有效的机制。
根据经验,迭代工作,缩短从概念到可工作、可发布的软件之间的时间,你必须:
一旦接受2环境中的所有验收测试都通过了,业务部门就可以决定向名为Cut的客户验收测试环境推送发布候选。在这个环境中,被选中的客户可以使用发布候选,向开发团队提供真实的反馈。再一次,由于接受1&接受2环境的反馈周期预计为1小时,所以Bug修复每天可以根据需要向过渡环境部署多次。
一旦发布候选部署到Cut,就会对它应用生产级监控和预警,和应用到生产环境的一样。
开发人员可以在Team Information Radiator上看着提交流经管道以及每个环境的当前状态。Information Radiator上的红色很容易吸引注意力,提示开发人员快速采取行动。
teamplay的不同团队采用我们这个持续交付模型的速度并不一样。有的团队在练习把TDD和结对作为日常工作模式。有的团队把BDD运用到了经过选择的特性上。这是一个渐进过程,不同的团队以不同的速率采用不同的方法。有的团队比较快,有的比较慢,这受许多技术和人为因素的影响。
几乎所有团队都采用了用户故事地图。我们有外部顾问和我们的产品经理结对,为即将到来的特性创建初始的用户故事地图。然后,我们让其他所有团队角色都参与到用户故事地图活动中,使他们熟悉这种方法。我们把便利贴粘在墙上做成离线用户故事地图,并使用合适的工具创建在线故事地图。工具支持至关重要,因为我们的团队在地理上是分散的。使用故事地图不仅能共享对用户旅途/目标的理解,而且事实证明,对制定发布优先级也非常有用,因为它使得优先级决策更容易,使每个团队成员都参与到这个过程。
有些团队已经实现了一个团队管道,而且正在优化不同环境的反馈周期。我们在团队中鼓励一种试验和自测量的文化,推动人们越来越多地采用这种方法,直到它变成所有团队面对所有新用户故事时的常规工作方法。
为了试验,有些团队每两周就会专门拿出一天来学习与工作相关的新东西。此外,所有的方法,如TDD
、BDD、结对、用户故事地图、DSL、验收测试、红-绿-重构-提交,最初都是被团队拿来在非常小的范围内试用,如在单个用户故事中。当团队在一个小范围内获得了对该方法的信任,看到了它的效果,如使用TDD实现了一个故事,他们就把方法的应用扩展到越来越多的故事。
不过,我们看到,看板和#NoEstimates很快就被大量采用。我们惊奇地发现,大部分团队都从Scrum切换到了看板,从冲刺估计切换到了#NoEstimates,而且是由他们自己在被赋予选择权限后几周之内完成的。
对于自测量,我们创建了一个供内部使用的CD成熟度模型,对于本文介绍的各种CD方法,团队自我评估采用情况。团队每月自评,事实证明,这有助于人们在头脑中把预期的开发过程放在首位,每月一小步向着它迭代。
此外,我们向团队提供了自动生成的仪表板,上面有衡量部署稳定性的CD指标。事实证明,以聚合方式查看上述方法是否恰当应用并产生了稳定的部署是很有用的。在有些团队中,可以使用这些指标指导CD转型。
我们还处在采用持续交付的起步阶段,但是,到目前为止,所采取的步骤已经产生了经过证明的成果!例如,在一个团队把TDD和结对运用到过去经常发现生产Bug的代码库区域中的新特性时,我们可以交付那个特性而没有任何生产Bug。
随着持续交付的实现,我们希望成为一个试验型组织。我们希望不再忙于交付充满假设的计划,相反,我们一直在生产环境中执行业务试验,快速验证假设。
这是指经常在生产环境中执行小试验,从技术和产品的角度尝试新想法,并把成功的试验不断地集成到产品中。
Dave Farley是持续交付、DevOps和软件开发领域的意见领袖。他是“震撼奖”获奖著作《持续交付》一书的合著者,他常常在大会上做演讲,他还是一名博主,同时也是反应式宣言的作者之一。Dave现在一名独立的软件开发人员和顾问,同时也是持续交付公司的创建者和负责人。你可以通过Twitter(@davefarley77)、博客或公司网站联系他。 | |
Dr. Vladyslav Ukis毕业于德国纽伦堡大学计算机科学系,后来是英国曼彻斯特大学。他每次毕业后都加入了Siemens Healthineers,一直致力于软件架构、企业架构、创新管理、私有和公有云计算、团队管理和工程管理等方面的工作。近年来,他一直在Siemens Healthineers数字生态系统平台推动持续交付转型,帮助一个大型的、分布式的、快速成长的开发团队采用新的工作方法,调整架构,实现所需的文化变革,保证系统在整个开发过程中总是处于可发布状态,实现持续交付。 |
查看英文原文:Adopting Continuous Delivery at teamplay, Siemens Healthineers