<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Jeff</title>
    <description>The latest articles on DEV Community by Jeff (@jefftian).</description>
    <link>https://dev.to/jefftian</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F318420%2F3bfd2d99-430c-4049-8dd5-e2adc961e1e0.png</url>
      <title>DEV Community: Jeff</title>
      <link>https://dev.to/jefftian</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/jefftian"/>
    <language>en</language>
    <item>
      <title>How to create a WeCom App to enable WeCom Login for the Web app</title>
      <dc:creator>Jeff</dc:creator>
      <pubDate>Wed, 15 May 2024 02:11:09 +0000</pubDate>
      <link>https://dev.to/jefftian/how-to-create-a-wecom-app-to-enable-wecom-login-for-the-web-app-539</link>
      <guid>https://dev.to/jefftian/how-to-create-a-wecom-app-to-enable-wecom-login-for-the-web-app-539</guid>
      <description>&lt;h2&gt;
  
  
  Login to WeCom Console
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://work.weixin.qq.com/" rel="noopener noreferrer"&gt;https://work.weixin.qq.com/&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Create an app
&lt;/h2&gt;

&lt;p&gt;Create a self-built app under the Apps tab:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ft29da6una3v89ypm7puf.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ft29da6una3v89ypm7puf.png" alt="Image description" width="800" height="784"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Note down app credentials
&lt;/h2&gt;

&lt;p&gt;Firstly, note down the AgentId, Then Click View and send secrets to WeCom:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frzxeuqcwargdlxmrfb18.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frzxeuqcwargdlxmrfb18.png" alt="Image description" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And note down the AppId and AppSecret from WeCom:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmdapgxcnoft82yuy9jsf.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmdapgxcnoft82yuy9jsf.png" alt="Image description" width="800" height="1732"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Specify the callback domain
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F76hj1hctn19odl390fml.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F76hj1hctn19odl390fml.png" alt="Image description" width="800" height="495"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpan20mbli6rstu0b87pl.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpan20mbli6rstu0b87pl.png" alt="Image description" width="800" height="469"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Configure the Trusted IP List
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0b88i4unhnwk5xsag5ry.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0b88i4unhnwk5xsag5ry.png" alt="Image description" width="800" height="750"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Done!&lt;/p&gt;

&lt;p&gt;After the integration, you can now log in through WeCom. The final result would be like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://www.zhihu.com/zvideo/1487480084424122368" rel="noopener noreferrer"&gt;https://www.zhihu.com/zvideo/1487480084424122368&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://www.zhihu.com/zvideo/1484138937099190272" rel="noopener noreferrer"&gt;https://www.zhihu.com/zvideo/1484138937099190272&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>oidc</category>
      <category>oauth</category>
    </item>
    <item>
      <title>Beyond the Code: The Undervalued Art of Communication in Tech Careers</title>
      <dc:creator>Jeff</dc:creator>
      <pubDate>Wed, 27 Mar 2024 05:52:37 +0000</pubDate>
      <link>https://dev.to/jefftian/beyond-the-code-the-undervalued-art-of-communication-in-tech-careers-5982</link>
      <guid>https://dev.to/jefftian/beyond-the-code-the-undervalued-art-of-communication-in-tech-careers-5982</guid>
      <description>&lt;p&gt;In the world of software development, the phrase "Talk is cheap, show me the code" has become a rallying cry for those who prioritize tangible results over mere discussion. It's a sentiment that resonates with the hacker ethos, emphasizing the value of working code over theoretical chatter. However, as many seasoned programmers have discovered through their careers, this axiom can be misleading and, in some contexts, detrimental to professional growth and influence in the workplace.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Misconception of Code as King
&lt;/h2&gt;

&lt;p&gt;The adage suggests that the only thing that truly matters in the tech industry is the ability to write good code. While technical proficiency is undoubtedly essential, it's a narrow viewpoint that fails to recognize the complexity of the software development process and the multifaceted nature of tech careers. This perspective can lure programmers into a false sense of security, believing that if they can just code well, success will naturally follow.&lt;/p&gt;

&lt;h2&gt;
  
  
  Communication: The Unseen Pillar of Success
&lt;/h2&gt;

&lt;p&gt;In reality, effective communication is often a more valuable currency than the ability to code. As developers climb the career ladder, they quickly realize that their impact and potential for advancement hinge not only on their technical skills but also on their ability to articulate ideas, collaborate with others, and influence decision-making processes.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Talk Isn't Cheap
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Teamwork and Collaboration&lt;/strong&gt;: Software development is rarely a solo endeavor. It requires constant communication with team members, stakeholders, and clients. The ability to discuss problems, brainstorm solutions, and synchronize efforts is critical to the success of any project.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Leadership and Influence&lt;/strong&gt;: As developers advance into leadership roles, their responsibilities shift from writing code to guiding teams and shaping the direction of projects. Influential leaders must be able to communicate a vision, motivate their team, and negotiate with other departments or clients.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Problem-Solving and Innovation&lt;/strong&gt;: Before any code is written, there must be a clear understanding of the problem at hand. Developers who can effectively communicate with non-technical stakeholders to identify the root of issues will be more adept at creating innovative solutions.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Mentorship and Knowledge Sharing&lt;/strong&gt;: Experienced developers are often expected to mentor juniors. The ability to explain complex concepts in an accessible manner is crucial for fostering the next generation of talent.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Career Advancement&lt;/strong&gt;: When it comes to promotions and career opportunities, those who can present their ideas compellingly and build relationships within and outside their organization will often have an edge.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  The Real Cost of Undervaluing Communication
&lt;/h2&gt;

&lt;p&gt;By undervaluing communication, developers limit their professional growth. They may become pigeonholed as "code machines," missing out on opportunities to contribute to strategic discussions and to take on more significant roles within their organizations.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Balance of Code and Conversation
&lt;/h2&gt;

&lt;p&gt;This is not to say that coding skills are unimportant; rather, it's a call to recognize the symbiotic relationship between code and communication. The most successful tech professionals understand the power of both. They know when to let their code do the talking and when to step up and articulate their thoughts.&lt;/p&gt;

&lt;p&gt;In conclusion, while "Talk is cheap, show me the code" captures the importance of concrete results, it paints an incomplete picture of what it takes to thrive in the tech industry. Communication is not just talk; it's the lifeblood of collaboration, leadership, and innovation. As developers navigate their careers, they would do well to invest as much in their communication skills as they do in their technical abilities, for in the landscape of professional growth, talk is not only not cheap—it's invaluable.&lt;/p&gt;

</description>
      <category>career</category>
      <category>programmers</category>
      <category>developers</category>
      <category>coding</category>
    </item>
    <item>
      <title>Pulumi Has Wowed Me</title>
      <dc:creator>Jeff</dc:creator>
      <pubDate>Wed, 13 Mar 2024 10:52:59 +0000</pubDate>
      <link>https://dev.to/jefftian/pulumi-has-wowed-me-37kh</link>
      <guid>https://dev.to/jefftian/pulumi-has-wowed-me-37kh</guid>
      <description>&lt;p&gt;As a programmer, there's always this urge to turn everything into code, hence the variety of xxx as code solutions. Even when it comes to diagramming, I prefer to use code, which led to the concept of diagram as code. I've discussed some related content in my previous article, "&lt;a href="https://zhuanlan.zhihu.com/p/389745846" rel="noopener noreferrer"&gt;Quickly Overviewing the Domain Model of Code with tplant - Jeff Tian's Article - Zhihu.&lt;/a&gt;"&lt;/p&gt;

&lt;p&gt;But that's not all. We even want to store our secrets in code repositories! Secret as code. Of course, we can't store them in plain text. Hence, SOPS is a convenient tool that allows us to track changes to secrets using git, as detailed in "&lt;a href="https://zhuanlan.zhihu.com/p/351520284" rel="noopener noreferrer"&gt;Encrypting Sensitive Information in Kubernetes Clusters - Jeff Tian's Article - Zhihu.&lt;/a&gt;"&lt;/p&gt;

&lt;p&gt;Is that enough? Not quite! We also hope to allocate and track changes to our infrastructure through code, so-called GitOps, or Infrastructure as code (IaC).&lt;/p&gt;

&lt;p&gt;How do you do IaC? Feel free to share in the comments.&lt;/p&gt;

&lt;p&gt;Let me start with how we do it.&lt;/p&gt;

&lt;p&gt;Initially, we took a rather basic approach, using shell or PowerShell scripts, which is purely imperative. It was a hassle to tweak the scripts and verify the changes simultaneously. It was quite a headache.&lt;/p&gt;

&lt;p&gt;Later, we moved on to Terraform, and since we primarily used AWS cloud, we also employed CloudFormation. This declarative approach significantly improved, though it fell short in the automation testing department. Moreover, writing the declarative files was somewhat dull; it wasn't programming but felt like writing HTML a decade ago. Strictly speaking, HTML is a markup language, not a programming language.&lt;/p&gt;

&lt;p&gt;After comparing various options, our team recently decided to use Pulumi for IaC. Although I'm still getting the hang of it, the experience has been quite impressive, particularly in the following aspects:&lt;/p&gt;

&lt;h2&gt;
  
  
  Secret as Code is even better than SOPS.
&lt;/h2&gt;

&lt;p&gt;As mentioned in "&lt;a href="https://zhuanlan.zhihu.com/p/351520284" rel="noopener noreferrer"&gt;Encrypting Sensitive Information in Kubernetes Clusters - Jeff Tian's Article - Zhihu&lt;/a&gt;," after learning about SOPS, I've used it in several real-world projects. However, a significant issue I encountered was often forgetting to encrypt secrets before committing, leading to plain text ending up in the code repository. While there are ways to avoid this, it still leaves room for error. With Pulumi, to my surprise, there's no need for SOPS. It has the same functionality as SOPS, encrypting secrets within the code instead of saving them in plain text. But it's simpler and leaves no room for error because it provides alternative ways to retrieve plain text secrets rather than displaying them in the files.&lt;/p&gt;

&lt;h2&gt;
  
  
  Automated Testing
&lt;/h2&gt;

&lt;p&gt;I never thought IaC could be like writing business logic with automated testing! That means you can now do TDD in your IaC projects! For example, you define the following secret information in your &lt;code&gt;secrets.json&lt;/code&gt; file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"secrets"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"pulumiSecretName"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"rds-credentials"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"awsSecretName"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"/rds/{env}-{region}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"createAwsSecret"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"kmsKeyIdOrAlias"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"keys"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"username"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"value"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"master"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"configKey"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"password"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"value"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"configKey"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"rds-password"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Secret storing credentials to RDS used by human users to access the database"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"tags"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"from"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"passwordstate"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="err"&gt;...&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the above secret definition, we added a tag. You can use automated tests to verify that the final deployed resources contain the expected tags before actually deploying to production:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Theory&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;ClassData&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;typeof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;StackTestData&lt;/span&gt;&lt;span class="p"&gt;))]&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt; &lt;span class="nf"&gt;Should_CreateSecretsWithProperTags&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;stackName&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// act&lt;/span&gt;
    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;resources&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;RunDeployment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;stackName&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;names&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;GetService&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;ManagedSecretsResourceNames&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;();&lt;/span&gt;

    &lt;span class="c1"&gt;// assert&lt;/span&gt;
    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;secrets&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;resources&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;OfType&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Secret&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;().&lt;/span&gt;&lt;span class="nf"&gt;ToList&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;expectedSecretsCount&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SecretsConfig&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Secrets&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Count&lt;/span&gt; &lt;span class="p"&gt;*&lt;/span&gt; &lt;span class="n"&gt;Config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DeploymentEnvironment&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Environments&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Count&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;secrets&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Count&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Should&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;Be&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;expectedSecretsCount&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;foreach&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;secret&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;secrets&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;secretName&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;secret&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Name&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetValue&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="c1"&gt;// ignore created secrets for different environments&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(!&lt;/span&gt;&lt;span class="n"&gt;secretName&lt;/span&gt;&lt;span class="p"&gt;!.&lt;/span&gt;&lt;span class="nf"&gt;Contains&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DeploymentEnvironment&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CurrentEnvironment&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Name&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;continue&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;secretConfig&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SecretsConfig&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Secrets&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Single&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;names&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;SecretName&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AwsSecretName&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="n"&gt;secretName&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;secretTags&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;secret&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Tags&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetValue&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="n"&gt;secretTags&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Should&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;NotBeNull&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="k"&gt;foreach&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;tag&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;Config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DefaultResourceTags&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;secretTags&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Should&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;Contain&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;secretTag&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;secretTag&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Key&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="n"&gt;tag&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Key&lt;/span&gt; &lt;span class="p"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;secretTag&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Value&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="n"&gt;tag&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Value&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="k"&gt;foreach&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;tag&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;secretConfig&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Tags&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;secretTags&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Should&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;Contain&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;secretTag&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;secretTag&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Key&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="n"&gt;tag&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Key&lt;/span&gt; &lt;span class="p"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;secretTag&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Value&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="n"&gt;tag&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Value&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="n"&gt;secretTags&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Should&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;Contain&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;secretTag&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;secretTag&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Key&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="s"&gt;"pulumi-secret-name"&lt;/span&gt; &lt;span class="p"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;secretTag&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Value&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="n"&gt;secretConfig&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;PulumiSecretName&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Easter Egg: The Unexpected AI Feature
&lt;/h2&gt;

&lt;p&gt;The team decided to use the C# version of Pulumi, which I found a bit daunting. However, I discovered that Pulumi offers an AI feature that answers any question I have. From what I've seen of the AI's guidance, it's focused on coding and does an incredibly reliable job of preventing any mishaps in the team (I'm still not fired at 35+).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhqmylwwp6yne1lgbteag.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhqmylwwp6yne1lgbteag.png" alt="Image description" width="800" height="457"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Even better, although it seems only to write code on the surface, this AI can do anything. For instance, when I come across English words I don't recognize, I ask it to translate:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F546pw72l9zqqadul42lb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F546pw72l9zqqadul42lb.png" alt="Image description" width="800" height="390"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;See? It can translate and even provides more thoughtful explanations than other AIs I've tried, including pinyin annotations!&lt;/p&gt;

&lt;p&gt;Of course, you can use it directly in Chinese as well because it understands Chinese:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frze8ttnilzyerbacqczh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frze8ttnilzyerbacqczh.png" alt="Image description" width="800" height="474"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The most crucial point is that unlike OpenAI's ChatGPT or many other foreign AI assistants inaccessible to users in China, it is available directly from within China!&lt;/p&gt;

&lt;p&gt;Another outrageous easter egg is that you can use it &lt;strong&gt;without logging in&lt;/strong&gt;! Can you believe that?&lt;/p&gt;

&lt;p&gt;Lastly, there are no limits whatsoever! It's incredibly generous; I've been using it for almost a month now, and it has never refused to answer my questions! There is no need to log in, let alone pay.&lt;/p&gt;

&lt;p&gt;In one word: Phenomenal!&lt;/p&gt;

</description>
      <category>iac</category>
      <category>pulumi</category>
      <category>genai</category>
    </item>
    <item>
      <title>Migrating Part of a Code Repository While Retaining the Full Commit History</title>
      <dc:creator>Jeff</dc:creator>
      <pubDate>Tue, 09 Jan 2024 06:41:33 +0000</pubDate>
      <link>https://dev.to/jefftian/migrating-part-of-a-code-repository-while-retaining-the-full-commit-history-4f75</link>
      <guid>https://dev.to/jefftian/migrating-part-of-a-code-repository-while-retaining-the-full-commit-history-4f75</guid>
      <description>&lt;p&gt;The purpose of this post is to explain how to migrate code with commit history using the tool &lt;code&gt;git-filter-repo&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Background
&lt;/h2&gt;

&lt;p&gt;Recently, I undertook a task that required migrating a specific folder from one project into a new code repository to preserve the commit history. After some effort, I finally discovered the &lt;code&gt;git-filter-repo&lt;/code&gt; tool, which perfectly accomplished this job. I am documenting this experience here.&lt;/p&gt;

&lt;h2&gt;
  
  
  Install &lt;code&gt;git-filter-repo&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;To start, install &lt;code&gt;git-filter-repo&lt;/code&gt;. The easiest way to do this on Mac is by using &lt;code&gt;brew&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;brew install git-filter-repo
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It is an open-source tool, and the source code can be found at &lt;a href="https://github.com/newren/git-filter-repo" rel="noopener noreferrer"&gt;https://github.com/newren/git-filter-repo&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Checkout a new branch
&lt;/h2&gt;

&lt;p&gt;Create a new branch based on &lt;code&gt;main&lt;/code&gt; branch in the original code repository for the partial migration.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;git checkout -b feature/JIRA-123-github-migration
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Filter the directories and files that need to be migrated
&lt;/h2&gt;

&lt;p&gt;If there are these files or directories that need to be migrated:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;File Lambda.sln &lt;/li&gt;
&lt;li&gt;File Lambda.Worker.sln.DotSettings.use&lt;/li&gt;
&lt;li&gt;Directory Worker&lt;/li&gt;
&lt;li&gt;Directory Worker.Tests&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you'd like to migrate them to folder &lt;code&gt;~/repos/NewLambda&lt;/code&gt;, then specify the files or directories that need to be migrated and the target folder using the &lt;code&gt;--path&lt;/code&gt; and &lt;code&gt;--target&lt;/code&gt; options, respectively.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;git filter-repo --path Lambda.sln --path Lambda.Worker.sln.DotSettings.user --path Worker --path Worker.Tests --target ~/repos/NewLambda
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Check result
&lt;/h2&gt;

&lt;p&gt;Confirm the migration results by checking if the directories have been successfully migrated and if the project files not in the migration scope are excluded. You can execute &lt;code&gt;ls&lt;/code&gt; under &lt;code&gt;~/repos/NewLambda&lt;/code&gt; folder.&lt;/p&gt;

&lt;p&gt;Verify the completeness of the commit history migration by using the command &lt;code&gt;git log --oneline&lt;/code&gt; in the target directory, &lt;code&gt;~/repos/NewLambda&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Push the migrated to remote
&lt;/h2&gt;

&lt;p&gt;If the migration is intended for GitHub, create a new repository on GitHub and add the remote origin to the local target directory.&lt;/p&gt;

&lt;p&gt;Push the migrated files to the new repository on GitHub.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;git push --set-upstream origin $(git branch --show-current)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Submit a PR
&lt;/h2&gt;

&lt;p&gt;Finally, submit a pull request from the migrated branch to the main branch for team members to review.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbacw3mdzcwwjv3u6xk2d.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbacw3mdzcwwjv3u6xk2d.png" alt="All commits are preserved after partially migrated" width="800" height="564"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;These steps are to migrate a code repository and retain the commit history using &lt;code&gt;git-filter-repo&lt;/code&gt;.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>How can non-tech people understand asymmetric encryption?</title>
      <dc:creator>Jeff</dc:creator>
      <pubDate>Tue, 19 Sep 2023 12:28:23 +0000</pubDate>
      <link>https://dev.to/jefftian/how-can-non-tech-people-understand-asymmetric-encryption-39e4</link>
      <guid>https://dev.to/jefftian/how-can-non-tech-people-understand-asymmetric-encryption-39e4</guid>
      <description></description>
      <category>encryption</category>
      <category>asymmetric</category>
    </item>
    <item>
      <title>How many total sock inspections should be expected to perform?</title>
      <dc:creator>Jeff</dc:creator>
      <pubDate>Wed, 14 Jun 2023 11:49:26 +0000</pubDate>
      <link>https://dev.to/jefftian/how-many-total-sock-inspections-should-be-expected-to-perform-3n7c</link>
      <guid>https://dev.to/jefftian/how-many-total-sock-inspections-should-be-expected-to-perform-3n7c</guid>
      <description>&lt;p&gt;Farhad has just washed and dried all his socks at the laundromat. Now he wants to pair the socks up. (Each sock shares a unique design with its pair.)&lt;/p&gt;

&lt;p&gt;Farhad starts with a total of 20 socks (10 pairs). He picks up and holds one sock in one hand. With the other hand, he picks up the other socks one-by-one until he finds the matching sock.&lt;/p&gt;

&lt;p&gt;How many total sock inspections should Farhad expect he'll perform to finish pairing 20 socks?&lt;/p&gt;

&lt;p&gt;The answer is 65.&lt;/p&gt;

&lt;p&gt;The exact number of socks Farhad picsk up to pair his first sock depends on luck. On average, we'd expect him to pick up 11 socks to match his first pair. This estimate comes from averageing the lucky and unlucky numbers. That average is 

&lt;span class="katex-element"&gt;
  &lt;span class="katex"&gt;&lt;span class="katex-mathml"&gt;20+22=11
\frac{20+2}{2}=11
&lt;/span&gt;&lt;span class="katex-html"&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mopen nulldelimiter"&gt;&lt;/span&gt;&lt;span class="mfrac"&gt;&lt;span class="vlist-t vlist-t2"&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="sizing reset-size6 size3 mtight"&gt;&lt;span class="mord mtight"&gt;&lt;span class="mord mtight"&gt;2&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="frac-line"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="sizing reset-size6 size3 mtight"&gt;&lt;span class="mord mtight"&gt;&lt;span class="mord mtight"&gt;20&lt;/span&gt;&lt;span class="mbin mtight"&gt;+&lt;/span&gt;&lt;span class="mord mtight"&gt;2&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-s"&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="mclose nulldelimiter"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mrel"&gt;=&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;11&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;/span&gt;
 if he has 20 socks.&lt;/p&gt;

&lt;p&gt;Similarly, for the 2nd pair we'd expect him to pick up 
&lt;span class="katex-element"&gt;
  &lt;span class="katex"&gt;&lt;span class="katex-mathml"&gt;18+22=10\frac{18+2}{2}=10 &lt;/span&gt;&lt;span class="katex-html"&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mopen nulldelimiter"&gt;&lt;/span&gt;&lt;span class="mfrac"&gt;&lt;span class="vlist-t vlist-t2"&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="sizing reset-size6 size3 mtight"&gt;&lt;span class="mord mtight"&gt;&lt;span class="mord mtight"&gt;2&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="frac-line"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="sizing reset-size6 size3 mtight"&gt;&lt;span class="mord mtight"&gt;&lt;span class="mord mtight"&gt;18&lt;/span&gt;&lt;span class="mbin mtight"&gt;+&lt;/span&gt;&lt;span class="mord mtight"&gt;2&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-s"&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="mclose nulldelimiter"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mrel"&gt;=&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;10&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;/span&gt;
 socks.&lt;/p&gt;

&lt;p&gt;And so on, and finally 2 to match the last pair. This comes out to a total of sock 
&lt;span class="katex-element"&gt;
  &lt;span class="katex"&gt;&lt;span class="katex-mathml"&gt;11+10+9+8+7+6+5+4+3+2=10⋅11+22=5⋅13=6511+10+9+8+7+6+5+4+3+2=10\cdot\frac{11+2}{2}=5 \cdot 13=65&lt;/span&gt;&lt;span class="katex-html"&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;11&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mbin"&gt;+&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;10&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mbin"&gt;+&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;9&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mbin"&gt;+&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;8&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mbin"&gt;+&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;7&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mbin"&gt;+&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;6&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mbin"&gt;+&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;5&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mbin"&gt;+&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;4&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mbin"&gt;+&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;3&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mbin"&gt;+&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;2&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mrel"&gt;=&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;10&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mbin"&gt;⋅&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mopen nulldelimiter"&gt;&lt;/span&gt;&lt;span class="mfrac"&gt;&lt;span class="vlist-t vlist-t2"&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="sizing reset-size6 size3 mtight"&gt;&lt;span class="mord mtight"&gt;&lt;span class="mord mtight"&gt;2&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="frac-line"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="sizing reset-size6 size3 mtight"&gt;&lt;span class="mord mtight"&gt;&lt;span class="mord mtight"&gt;11&lt;/span&gt;&lt;span class="mbin mtight"&gt;+&lt;/span&gt;&lt;span class="mord mtight"&gt;2&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-s"&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="mclose nulldelimiter"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mrel"&gt;=&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;5&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mbin"&gt;⋅&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;13&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mrel"&gt;=&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;65&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;/span&gt;
pick-ups.&lt;/p&gt;

</description>
      <category>math</category>
    </item>
    <item>
      <title>Use TypeScript to make DDD come true</title>
      <dc:creator>Jeff</dc:creator>
      <pubDate>Thu, 22 Jul 2021 11:54:37 +0000</pubDate>
      <link>https://dev.to/jefftian/use-typescript-to-make-ddd-come-true-4ff0</link>
      <guid>https://dev.to/jefftian/use-typescript-to-make-ddd-come-true-4ff0</guid>
      <description>&lt;h2&gt;
  
  
  DDD
&lt;/h2&gt;

&lt;p&gt;Domain Driven Design is popular and even became a de facto standard for enterprise technology team, however, it's only showcases, not the real implementation. How many times that you see the fancy design diagrams and at them same the poor code? &lt;/p&gt;

&lt;p&gt;Many people prefers design first, what implies that code is the implementation, and only the slide shows are design. But actually, code is design.&lt;/p&gt;

&lt;h2&gt;
  
  
  Code = Design = Model = Documents
&lt;/h2&gt;

&lt;p&gt;If not the case, then everytime you change the code you need to update the UML class diagrams and database E-R design documents. And eventually these kind of documents becomes unreliable gradually because you can't make sure that they are updated in time.&lt;/p&gt;

&lt;p&gt;In fact, code is more suitable for expressing the designs, and source code is a document indeed, can be used to describe the current product's design decisions perfectly.&lt;/p&gt;

&lt;p&gt;If developer created a domain model by code which is consistent with what in a domain expert's brain, then the source code is the most efficient, realtime model no doubtedly.&lt;/p&gt;

&lt;p&gt;The limit of the equation of &lt;code&gt;Code = Deisgn = Model = Documents&lt;/code&gt; is whether the domain expert can read the code. So the easy to learn, expressive and intuitive programming language will have strong benefits in the process of creating domain models.&lt;/p&gt;

&lt;h2&gt;
  
  
  Domain Modelling
&lt;/h2&gt;

&lt;p&gt;Domain modelling is the most important part in DDD for developers as it requires developers have good abstraction, and it differs from traditional database modelling, and need developers to map the domain knowledge into the code models through the most efficient programming technic.&lt;/p&gt;

&lt;p&gt;In the long run, the object oriented language is the first choice of domain modelling, some OO skills can be used to do the domain model abstraction. In the contrary, the functional programming langauges are typically thought to be suitable only for data processing, scientific computing, etc.&lt;/p&gt;

&lt;h2&gt;
  
  
  TypeScript
&lt;/h2&gt;

&lt;p&gt;But this article shows that TypeScript, which has many functional programming features (&lt;br&gt;
TypeScript's type system fully meets the functional programming requirements), can be used to do the domain modelling, and thanks to its type system and related tool chains, TypeScript should be considered as the best language to make DDD landing.&lt;/p&gt;

&lt;h2&gt;
  
  
  TypeScript's type system
&lt;/h2&gt;

&lt;p&gt;Comparing to OO, you only need to know a few grammer and it's enough to start domain modelling, so in terms of simplicity, algebraic data type is more suitable for domain modelling so as to make the domain models be documents.&lt;/p&gt;

&lt;h2&gt;
  
  
  Type
&lt;/h2&gt;

&lt;p&gt;All kinds of programming languages provide primitive types by design, such as &lt;code&gt;string&lt;/code&gt;, &lt;code&gt;bool&lt;/code&gt;, &lt;code&gt;number&lt;/code&gt;, etc.&lt;/p&gt;

&lt;p&gt;In TypeScript, you can use the keyword &lt;code&gt;type&lt;/code&gt; to compose larger types:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;

&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;Name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;firstName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;
  &lt;span class="na"&gt;middleName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;
  &lt;span class="na"&gt;lastName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;The usage of the above is obviouse, and besides this kind of usage, the keyword &lt;code&gt;type&lt;/code&gt; has other usages, which is not a trivial feature. It can help you record the domain knowledge into domain models, for example:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;timeToFly&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;you can not guess the domain knowledge at first glance at the above code. How to make sure what the 10 means? Look up in a document? No, you need to tell yourself that code is document, so you improve your code as follows:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;

&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;Second&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;timeToFly&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Second&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h2&gt;
  
  
  Type Or
&lt;/h2&gt;

&lt;p&gt;In TypeScript, they are called as Union Types, which can be built by the symbol &lt;code&gt;|&lt;/code&gt;:&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;

&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;Pet&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;Fish&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nx"&gt;Bird&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;So &lt;code&gt;Pet&lt;/code&gt; is in type &lt;code&gt;Fish&lt;/code&gt; or &lt;code&gt;Bird&lt;/code&gt;. In general, functional programming languages have strong pattern match capability to process this kind of type. But the sad thing is TypeScript has limited pattern match capability so you can often see some string literals present in the types to distinguish different types.&lt;/p&gt;

&lt;h2&gt;
  
  
  Type And
&lt;/h2&gt;

&lt;p&gt;In TypeScript, they are called as Intersection Types, which can be built by the symbol &lt;code&gt;&amp;amp;&lt;/code&gt;: &lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;

&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;ABC&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;A&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;B&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;C&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;The above code tries to say that type ABC contains all A, B and C's properties.&lt;/p&gt;

&lt;h2&gt;
  
  
  Define Function Types
&lt;/h2&gt;

&lt;p&gt;In TypeScript, there were no differences between function and other types, so you can define functions by using the keyword &lt;code&gt;type&lt;/code&gt;:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;

&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;Add&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;The above code shows that &lt;code&gt;Add&lt;/code&gt; is a function who accepts 2 numbers as arguments and returns a number.&lt;/p&gt;

&lt;h2&gt;
  
  
  Using code to share domain knowledge
&lt;/h2&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;

&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;CreditCard&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;cardNo&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;
  &lt;span class="na"&gt;firstName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;
  &lt;span class="na"&gt;middleName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;
  &lt;span class="na"&gt;lastName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;
  &lt;span class="na"&gt;contactEmail&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Email&lt;/span&gt;
  &lt;span class="na"&gt;contactPhone&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Phone&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Notice that we can easily write the above code by just having the knowledge showed previously, to describe the &lt;code&gt;CreditCard&lt;/code&gt; payment method. Also please notice we don't use &lt;code&gt;class&lt;/code&gt; here.&lt;/p&gt;

&lt;p&gt;But is it a reliable domain model? If not, where is the problem?&lt;/p&gt;

&lt;p&gt;The most serious problem of the above code is that it didn't record the domain knowledge which should be owned by it inside of it. Let me ask some questions:&lt;/p&gt;

&lt;p&gt;Question: can &lt;code&gt;middle name&lt;/code&gt; be empty?&lt;br&gt;
Answer 1: Not sure, need to check document.&lt;br&gt;
Answer 2: Maybe? &lt;code&gt;middle name&lt;/code&gt; can be null.&lt;/p&gt;

&lt;h2&gt;
  
  
  Modelling for the nullable type
&lt;/h2&gt;

&lt;p&gt;In functional programming languages, the nullable types can be defined as Optional. Although null is valid in TypeScript (Note: we can enable &lt;code&gt;strictNullChecks&lt;/code&gt; to enforece the null check), but in functional programming, you can only use Optional type to express nullable type.&lt;/p&gt;

&lt;p&gt;If the domain expert tells you that &lt;code&gt;middle name&lt;/code&gt; can exists, or be empty. Plese notice the word "or", indicate that we can use Union Type to model for the nullable type:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;

&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;Optional&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;T&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;A simple Optional is just a Type Or. The improved code looks as follows:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;

&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;CreditCard&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;cardNo&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;
  &lt;span class="na"&gt;firstName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;
  &lt;span class="na"&gt;middleName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Option&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="na"&gt;lastName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;
  &lt;span class="na"&gt;contactEmail&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Email&lt;/span&gt;
  &lt;span class="na"&gt;contactPhone&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Phone&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h2&gt;
  
  
  Avoid Primitive Obsession
&lt;/h2&gt;

&lt;p&gt;Question: Can we express &lt;code&gt;cardNo&lt;/code&gt; with &lt;code&gt;string&lt;/code&gt;? If so can it be any string? Is &lt;code&gt;firstName&lt;/code&gt; an arbitrary length string? Obviously you can't answer these questions as this model doesn't contain relative domain knowledge.&lt;/p&gt;

&lt;p&gt;You may use &lt;code&gt;string&lt;/code&gt; type for &lt;code&gt;cardNo&lt;/code&gt; during programming, but in domain model, &lt;code&gt;string&lt;/code&gt; can't express the domain knowledge of &lt;code&gt;cardNo&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;cardNo&lt;/code&gt; is a 19-length string starts with &lt;code&gt;200&lt;/code&gt;, &lt;code&gt;name&lt;/code&gt; is a string whose length is less than or equal to 50. As such, the domain information can be implemented by &lt;code&gt;type alias&lt;/code&gt;:&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;

&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;CardNo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;
&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;Name50&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;With the above types, you now have chance to include the &lt;code&gt;cardNo&lt;/code&gt; business rules inside domain models by defining function.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;

&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;GetCardNo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;cardNo&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;CardNo&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;If a user typed in a string with 20 length, then what will the function &lt;code&gt;GetCardNo&lt;/code&gt; return? null? or exception thrown? Actually functionaly programming has more elegant way such as Either Monad or Railway oriented programming to handle errors. At least we can present the function's signature by Optional:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;

&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;GetCardNo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;cardNo&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;Optional&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;CardNo&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;The function expresses the validation process clearly, if you user typed in a string, then returns a CardNo type or empty.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;

&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;CreditCard&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;cardNo&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Optional&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;CardNo&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="na"&gt;firstName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Name50&lt;/span&gt;
  &lt;span class="na"&gt;middleName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Optional&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="na"&gt;lastName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Name50&lt;/span&gt;
  &lt;span class="na"&gt;contactEmail&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Email&lt;/span&gt;
  &lt;span class="na"&gt;contactPhone&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Phone&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;So now the code is full of domain knowledge, and these types can be used as unit tests as well. For example, you'll never assign an email to contactPhone, as they are not string, so in turn they represent different domain knowledge.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Atomicity and Composibility of the domain models
&lt;/h2&gt;

&lt;p&gt;There were 3 names in the above domain model, can they be changed separately? for example, change &lt;code&gt;middle name&lt;/code&gt; only? If not how can we encapsulate the knowledge of atomicity change into the domain model?&lt;/p&gt;

&lt;p&gt;In fact we can easily extract &lt;code&gt;Name&lt;/code&gt; and &lt;code&gt;Contact&lt;/code&gt; types and compose them:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;

&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;Name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;firstName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Name50&lt;/span&gt;
  &lt;span class="na"&gt;middleName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Option&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="na"&gt;lastName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Name50&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;Contact&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;contactEmail&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Email&lt;/span&gt;
  &lt;span class="na"&gt;contactPhone&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Phone&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;CreditCard3&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;cardNo&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Optional&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;CardNo&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Name&lt;/span&gt;
  &lt;span class="na"&gt;contact&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Contact&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h2&gt;
  
  
  Make the error state can't be present
&lt;/h2&gt;

&lt;p&gt;There is an important principle in domain modelling, which can be understood as: The domain models you built should have as many static validations and constraints as possible to make error occurs in compilation time instead of run time, so as to avoid the chance for mistakes. In fact all the domain modellings are following this principle, for example, the Email type and Phone type in the above code. Why not use string? Because string is lakcing of domain knowledges, which gives developers chances to make mistakes.&lt;/p&gt;

&lt;p&gt;Let's see another example. The above domain model has a contact type, which contains an Email and Phone properties. After payment done, system can utilize these 2 properties to send notification to user, so there is a rule generated: User must fill in Email or Phone to receive payment messages.&lt;/p&gt;

&lt;p&gt;First of all, the above domain models are not matching this business rule, because both Email and Phone are non-nullable type, which means these 2 properties are both required.&lt;/p&gt;

&lt;p&gt;Can we change both of them to be Optional?&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;

&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;Contact&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;contactEmail&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Option&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Email&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="na"&gt;contactPhone&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Option&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Phone&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Obviousely we can't do this, as it violets the principle of Make illegal state unrepresentable, so gives chances for coding mistakes. Your domain model represents an illegal state, that both Email and Phone can be empty. You may argue that my xxService will do the validation, to make sure they'll never be both empty. Sorry, we hope our domain model can encapsulate this domain knowledge. For xxService, it's unrelated to domain model. So can we express this rule in the modle model or not? The answer is yes! Because there is a "or" in the rule, so it implies that we can use the type Or (union type) to express this relationship:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;

&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;OnlyContactEmail&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;Email&lt;/span&gt; 
&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;OnlyContactPhone&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;Phone&lt;/span&gt;
&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;BothContactEmailAndPhone&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;Email&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;Phone&lt;/span&gt;
&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;Contact&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; 
  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nx"&gt;OnlyContactEmail&lt;/span&gt;
  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nx"&gt;OnlyContactPhone&lt;/span&gt;
  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nx"&gt;BothContactEmailAndPhone&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;By using TypeScript to guide the domain modelling, we can avoid classes and sub classes, let alone the keywords &lt;code&gt;abstract&lt;/code&gt; and &lt;code&gt;bean&lt;/code&gt;, etc.&lt;/p&gt;

&lt;p&gt;To measure how good or how bad a domain model is, we need to judge&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;is the domain model contains as many domain knowledges as possible, can it map the domain models inside domain experts' brains?&lt;/li&gt;
&lt;li&gt;Can domain model itself be the documents, so everyone can share and communicate with it?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;At the mean time, a framework should have as less jargons as possible. For example if you created a domain model class named &lt;code&gt;AbstractContactBase&lt;/code&gt;, you increased the complexity of the system without any help on domain models sharing.&lt;/p&gt;
&lt;h2&gt;
  
  
  Bonus
&lt;/h2&gt;

&lt;p&gt;If you use TypeScript, not only you can build a rich model models, but also you can utilize some tools to generate UMLs from the code. So you can birdview the project's domain models very quickly!&lt;/p&gt;
&lt;h2&gt;
  
  
  tplant
&lt;/h2&gt;

&lt;p&gt;tplant is that kind of tool which I participated in it.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9okp5nazpqfhv8tbnnr2.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9okp5nazpqfhv8tbnnr2.jpg" alt="tplant in github"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Screen recording of usage
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F849de7pmnompjdxc2lt6.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F849de7pmnompjdxc2lt6.gif" alt="Screen recording of using tplant"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Text version of usage
&lt;/h2&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--global&lt;/span&gt; tplant
&lt;span class="nb"&gt;cd &lt;/span&gt;your-typescript-project-folder
tplant &lt;span class="nt"&gt;--input&lt;/span&gt; src/&lt;span class="k"&gt;**&lt;/span&gt;/&lt;span class="k"&gt;*&lt;/span&gt;.ts &lt;span class="nt"&gt;--output&lt;/span&gt; output.svg
open output.svg


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

</description>
      <category>typescript</category>
      <category>tplant</category>
      <category>ddd</category>
      <category>uml</category>
    </item>
    <item>
      <title>Functional style programming is awesome (isomorphic example)</title>
      <dc:creator>Jeff</dc:creator>
      <pubDate>Mon, 08 Mar 2021 05:23:27 +0000</pubDate>
      <link>https://dev.to/jefftian/functional-style-programming-is-awesome-isomorphic-example-4b9p</link>
      <guid>https://dev.to/jefftian/functional-style-programming-is-awesome-isomorphic-example-4b9p</guid>
      <description>&lt;p&gt;This post will present a look and feel about functional style programming, gives you a glance about what it would look like if we wrote programs in a functional programming way.&lt;/p&gt;

&lt;p&gt;It's not a real strict functional programming guide, it just shows how interesting yet powerful if we handle problems in a functional programming mind.&lt;/p&gt;

&lt;h2&gt;
  
  
  Problem
&lt;/h2&gt;

&lt;p&gt;It's quite common challenge to ask you &lt;a href="https://leetcode.com/problems/isomorphic-strings/description/"&gt;implement an algorithm to detect if 2 strings are isomorphic&lt;/a&gt; during a programming job interview, there might be many answers on it. Let's do it again.&lt;/p&gt;

&lt;h2&gt;
  
  
  Tools
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;A browser. So we can write pure JavaScript to implement it by pressing F12 with a running brower at hand.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8tb9j3a9ot1sxqnrkgug.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8tb9j3a9ot1sxqnrkgug.png" alt="image" width="800" height="601"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;By analyzing the requirement, we can see actually the term &lt;code&gt;isomorphic&lt;/code&gt; reflects the requirement quite well, which means having the same form, in other words, the forms (or structures) in a way are the same(equal). So we can just write some code to express the meanings:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;isomorphic&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;equalBy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;structure&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So far we have the signature of the function &lt;code&gt;equalBy&lt;/code&gt;, let's implement it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;equalBy&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;fn&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;fn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nf"&gt;fn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It's natural and self expressed.&lt;/p&gt;

&lt;p&gt;Now we take a closer look into &lt;code&gt;isomorphic&lt;/code&gt;, we found it cares only the structure of the string, and doesn't give a shit to the detail characters in it. So how to we express the form (or structure) of the string?&lt;/p&gt;

&lt;p&gt;By examining the examples given in the requirement we come up with an idea to express the structure of a string by the character indices in the string, which can be expressed by numbers so it abstracts from the detail characters. So we write the following code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;structure&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;s&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[...&lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;indexOf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;-&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This line of code is a little bit long, let's test it and document it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;assert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;structure&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;aabbcc&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;002244&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;A structure of a string can be expressed through the indices of the characters in it&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;By far we have both &lt;code&gt;equalBy&lt;/code&gt; and &lt;code&gt;structure&lt;/code&gt;, so &lt;code&gt;isomorphic&lt;/code&gt; is ready to run! We can write some tests to show it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;assert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;isomorphic&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;empty strings are isomorphic&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;assert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;isomorphic&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;aabbcc&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;aabbcc&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;strings are always isomorphic with themselves&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;assert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;isomorphic&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;aabbcc&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;zzxxyy&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;if the characters have the same indices sequence, then the strings composed by them are isomorphic&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;assert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nf"&gt;isomorphic&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;aabacc&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;xxyyzz&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;even if the character indices are the same, however the sequences are not all the same, then the 2 strings composed by them are NOT isomorphic&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;assert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nf"&gt;isomorphic&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;aaabbcc&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;xxyyyzz&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;if any character indices are different, then the strings composed by them are NOT isomorphic&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;assert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nf"&gt;isomorphic&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;abcdefghijk&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;abcdefghijba&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;if the lengths are different, then the strings are NOT isomorphic&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We ran the tests, all pass!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmmq4ulxbek2thg4vir97.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmmq4ulxbek2thg4vir97.png" alt="All tests pass" width="800" height="452"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;So the implementation code for &lt;code&gt;isomorphic&lt;/code&gt; is only 3 lines in total:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;equalBy&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;fn&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;fn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nf"&gt;fn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;structure&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;s&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[...&lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;indexOf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;-&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;isomorphic&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;equalBy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;structure&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can see it's a &lt;code&gt;pointless&lt;/code&gt; way of writing code, besides cool, it solves problem elegantly even to a simple extend!&lt;/p&gt;

&lt;p&gt;You can try on your browser or check it in &lt;a href="https://leetcode.com/submissions/detail/530009145/"&gt;leetcode&lt;/a&gt; too: &lt;del&gt;&lt;a href="https://leetcode.com/submissions/detail/465004270/"&gt;https://leetcode.com/submissions/detail/465004270/&lt;/a&gt;&lt;/del&gt; &lt;a href="https://leetcode.com/submissions/detail/530009145/"&gt;https://leetcode.com/submissions/detail/530009145/&lt;/a&gt;&lt;/p&gt;

</description>
      <category>functional</category>
      <category>programming</category>
      <category>javascript</category>
    </item>
    <item>
      <title>How to get logs from all pods of a service in a cluster?</title>
      <dc:creator>Jeff</dc:creator>
      <pubDate>Wed, 10 Feb 2021 02:38:18 +0000</pubDate>
      <link>https://dev.to/jefftian/how-to-get-logs-from-all-pod-of-a-service-in-a-cluster-30mf</link>
      <guid>https://dev.to/jefftian/how-to-get-logs-from-all-pod-of-a-service-in-a-cluster-30mf</guid>
      <description>&lt;h2&gt;
  
  
  Background
&lt;/h2&gt;

&lt;p&gt;You deployed your app or service to a kubernetes cluster and with multiple instances, and you want to quickly search some logs by keyword. But the service is running on multiple instances in the cluster, so we need a way to gather the relevant logs from all instances instead of pod by pod.&lt;/p&gt;

&lt;h2&gt;
  
  
  Fake "Solutions"
&lt;/h2&gt;

&lt;p&gt;By googling you might get some answers like&lt;br&gt;
&lt;br&gt;
 &lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;kubectl logs service/your-service-name | grep "xxx"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;But this command is not you want, because it just search the log from one pod among the multiple instances. So if you get empty result it doesn't mean there were no relevant logs.&lt;/p&gt;

&lt;p&gt;Or someone encourage you execute the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;kubectl logs -l app=your-app-name | grep "xxx"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This might or might not work, it depends on how the app name was defined in the &lt;code&gt;deployment&lt;/code&gt; file, if the app names are different across the instances, because for example, an unique suffix will be appended to the common readable name for each pod. In this case you will never get any logs by the previous command.&lt;/p&gt;

&lt;h2&gt;
  
  
  Better way
&lt;/h2&gt;

&lt;p&gt;If your deployment file gives unique names to each instance, then you need to first get which labels are fixed across different instances. For example, you can randomly choose a pod, and execute:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;kubectl get pod/your-pod -o template --template='{{.metadata.labels}}'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This command will give you result similar to&lt;br&gt;
&lt;br&gt;
 &lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;map[branch:develop namespace:jeff-tian pod-template-hash:54ff684c58 role:cool-app run:cool-app update-timestamp:1608695126059]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;From the result we can see that the role is fixed, so we can get the logs in the following way:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;kubectl logs --selector role=cool-app | grep "xxx"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Even better way
&lt;/h2&gt;

&lt;p&gt;Send logs to Elasticsearch, and query the logs through Kibana. But this way requires the deployment and integration with ELK, and if you send the logs in real time (for example, using pinojs/pino-elasticsearch), then there is no log latency, only need to change a few application code. If you don't want to change the code, then you can use, filebeats for example, to gather the logs, but in this way you need to solve the log latency challenge. &lt;/p&gt;

&lt;p&gt;Comparing to get the logs by the command line, ELK way is obvious more powerful yet need more work to do, which worth a dedicate post to share it in the future.&lt;/p&gt;

</description>
      <category>kubernetes</category>
      <category>pod</category>
      <category>service</category>
      <category>log</category>
    </item>
    <item>
      <title>InversifyJS 中文文档</title>
      <dc:creator>Jeff</dc:creator>
      <pubDate>Thu, 31 Dec 2020 12:12:00 +0000</pubDate>
      <link>https://dev.to/jefftian/inversifyjs-2de0</link>
      <guid>https://dev.to/jefftian/inversifyjs-2de0</guid>
      <description>&lt;p&gt;你的项目中有用到 JavaScript 吗？如果有，那么依赖注入是怎么实现的？强烈推荐使用 &lt;a href="https://doc.inversify.cloud/zh_cn"&gt;InversifyJS&lt;/a&gt;！&lt;/p&gt;

&lt;p&gt;2020年最后一天，工作效率可以说是非常高了。&lt;br&gt;
早上买完&lt;a href="https://doc.inversify.cloud/zh_cn"&gt;域名&lt;/a&gt;（&lt;a href="https://doc.inversify.cloud/zh_cn"&gt;https://doc.inversify.cloud/zh_cn&lt;/a&gt; ），开始翻译 InversifyJS 文档，到下班时间终于翻译完成。加班部署上线，现在新鲜出炉的&lt;a href="https://doc.inversify.cloud/zh_cn"&gt;中文文档&lt;/a&gt;来了！&lt;/p&gt;

&lt;p&gt;InversifyJS 小巧优雅又五脏俱全，可以说是一个相当棒的轮子了。如果用得爽，想自己造一个又不知道如何下手，那么：&lt;/p&gt;

&lt;p&gt;给你一个 Bonus：这篇文章手把手教你从零开始撸一个 Toy Inversify：&lt;a href="https://jeff-tian.jiwai.win/posts/inversion-of-control-implementation-based-on-typescript-3fl6/"&gt;https://jeff-tian.jiwai.win/posts/inversion-of-control-implementation-based-on-typescript-3fl6/&lt;/a&gt; ，快来看一看吧！&lt;/p&gt;

</description>
      <category>inversifyjs</category>
      <category>ioc</category>
      <category>container</category>
      <category>di</category>
    </item>
    <item>
      <title>Connect to external mysql server via phpMyAdmin</title>
      <dc:creator>Jeff</dc:creator>
      <pubDate>Mon, 21 Dec 2020 07:43:20 +0000</pubDate>
      <link>https://dev.to/jefftian/connect-to-external-mysql-server-via-phpmyadmin-188l</link>
      <guid>https://dev.to/jefftian/connect-to-external-mysql-server-via-phpmyadmin-188l</guid>
      <description>&lt;h2&gt;
  
  
  Background
&lt;/h2&gt;

&lt;p&gt;After I deployed my phpMyAdmin to my kubernetes cluster and ran it, I was astonished by the UI, which only allows you type username and password, there is no way to input my mysql server's host!&lt;/p&gt;

&lt;h2&gt;
  
  
  Analysis
&lt;/h2&gt;

&lt;p&gt;Seems phpMyAdmin by default only allows you connect to mysql server deployed in the same machine with the phpMyAdmin itself, in terms of this I would say I love adminer more!&lt;/p&gt;

&lt;h2&gt;
  
  
  Solution
&lt;/h2&gt;

&lt;p&gt;You need to set &lt;code&gt;PMA_HOST&lt;/code&gt; to your mysql server host before running the phpMyAdmin instance.&lt;/p&gt;

&lt;p&gt;So if you are deploying your phpMyAdmin to your kubernetes cluster, you should edit the &lt;code&gt;deployment.yaml&lt;/code&gt; as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;apps/v1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Deployment&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;labels&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;app&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;phpmyadmin&lt;/span&gt;
    &lt;span class="na"&gt;tier&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;backend&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;phpmyadmin&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;minReadySeconds&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;5&lt;/span&gt;
  &lt;span class="na"&gt;selector&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;matchLabels&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;app&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;phpmyadmin&lt;/span&gt;
      &lt;span class="na"&gt;tier&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;backend&lt;/span&gt;
  &lt;span class="na"&gt;template&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;labels&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;app&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;phpmyadmin&lt;/span&gt;
        &lt;span class="na"&gt;tier&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;backend&lt;/span&gt;
    &lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;imagePullSecrets&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;harbor-odp&lt;/span&gt;
      &lt;span class="na"&gt;containers&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;phpmyadmin"&lt;/span&gt;
          &lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;PMA_HOST&lt;/span&gt;
              &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;your-my-sql-db.yourhost.com.cn&lt;/span&gt;
          &lt;span class="na"&gt;imagePullPolicy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Always&lt;/span&gt;
          &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;phpmyadmin&lt;/span&gt;
          &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;containerPort&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;80&lt;/span&gt;
              &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;http&lt;/span&gt;
              &lt;span class="na"&gt;protocol&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;TCP&lt;/span&gt;
          &lt;span class="na"&gt;resources&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;limits&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
              &lt;span class="na"&gt;cpu&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;500m&lt;/span&gt;
              &lt;span class="na"&gt;memory&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;128Mi&lt;/span&gt;
            &lt;span class="na"&gt;requests&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
              &lt;span class="na"&gt;cpu&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;250m&lt;/span&gt;
              &lt;span class="na"&gt;memory&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;64Mi&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>phpmyadmin</category>
      <category>pma</category>
      <category>host</category>
    </item>
    <item>
      <title>[Solved] adb install successfully but can't open the app on Huawei phone</title>
      <dc:creator>Jeff</dc:creator>
      <pubDate>Mon, 07 Dec 2020 10:15:44 +0000</pubDate>
      <link>https://dev.to/jefftian/solved-adb-install-successfully-but-can-t-open-the-app-on-huawei-phone-3kc6</link>
      <guid>https://dev.to/jefftian/solved-adb-install-successfully-but-can-t-open-the-app-on-huawei-phone-3kc6</guid>
      <description>&lt;h2&gt;
  
  
  Issue
&lt;/h2&gt;

&lt;p&gt;You installed the apk successfully to your huawei phone via &lt;code&gt;adb install&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;adb install xxx.apk
Performing Streamed Install
Success
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But after the installation you can't open it as the open button is disabled, nor can you find or search it in your phone.&lt;/p&gt;

&lt;h2&gt;
  
  
  Analyse
&lt;/h2&gt;

&lt;p&gt;There is a private space feature in huawei phone, and you turned it on.&lt;/p&gt;

&lt;h2&gt;
  
  
  Solution
&lt;/h2&gt;

&lt;p&gt;Go to settings/privacy, you'll see the private space. Switch into that space and you can find the app.&lt;/p&gt;

&lt;p&gt;Or you can delete the private space totally to avoid this issue in the future.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fszj617bxgx01uu86n2rj.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fszj617bxgx01uu86n2rj.jpeg" alt="Private Space" width="800" height="1662"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>adb</category>
      <category>apk</category>
      <category>huawei</category>
      <category>android</category>
    </item>
  </channel>
</rss>
