<?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: Patrick Gramatowski</title>
    <description>The latest articles on DEV Community by Patrick Gramatowski (@rwxpat).</description>
    <link>https://dev.to/rwxpat</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%2F2419141%2F27d118ce-0e79-401e-8606-274874d431d4.jpeg</url>
      <title>DEV Community: Patrick Gramatowski</title>
      <link>https://dev.to/rwxpat</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/rwxpat"/>
    <language>en</language>
    <item>
      <title>[SECURITY IN RAILS] Preventing enumeration attacks, data leaks, and timing based attacks 🔐🛤️</title>
      <dc:creator>Patrick Gramatowski</dc:creator>
      <pubDate>Tue, 03 Jun 2025 19:49:49 +0000</pubDate>
      <link>https://dev.to/rwxpat/security-in-rails-preventing-enumeration-attacks-data-leaks-and-timing-based-attacks-4k6e</link>
      <guid>https://dev.to/rwxpat/security-in-rails-preventing-enumeration-attacks-data-leaks-and-timing-based-attacks-4k6e</guid>
      <description>&lt;h3&gt;
  
  
  Enumeration attacks 🕵️‍♂️
&lt;/h3&gt;

&lt;p&gt;Enumeration attacks are a class of security vulnerabilities where attackers exploit differences in system responses to infer valid user information. In authentication systems, this often occurs when the application provides distinct error messages - such as “&lt;em&gt;Invalid username&lt;/em&gt;” versus “&lt;em&gt;Invalid password&lt;/em&gt;”. Such discrepancies &lt;strong&gt;enable attackers to identify valid usernames, paving the way for brute-force or dictionary attacks on passwords&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Beyond the technical risk, this also represents a privacy issue. User identifiers like email addresses are often structured (e.g., &lt;a href="mailto:name.surname@domain.com"&gt;name.surname@domain.com&lt;/a&gt;), which can &lt;strong&gt;reveal personal information&lt;/strong&gt;. In certain applications, especially those handling sensitive user data - exposing whether an email exists can be a &lt;strong&gt;significant breach of user privacy&lt;/strong&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%2F7gylzv65oa69qvggss4r.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%2F7gylzv65oa69qvggss4r.png" alt="Enumeration Attacks" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;source: &lt;a href="https://www.upguard.com/blog/what-is-an-enumeration-attack" rel="noopener noreferrer"&gt;upguard.com&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://cheatsheetseries.owasp.org/cheatsheets/Authentication_Cheat_Sheet.html#authentication-and-error-messages" rel="noopener noreferrer"&gt;OWASP on Authentication and Error Messages&lt;/a&gt;: '&lt;em&gt;Incorrectly implemented error messages in the case of authentication functionality can be used for the purposes of user ID and password enumeration. An application should respond (both HTTP and HTML) in a generic manner.&lt;/em&gt;'&lt;/p&gt;

&lt;h4&gt;
  
  
  Prevent enumeration attacks and data leaks by changing error messages to generic:
&lt;/h4&gt;

&lt;p&gt;For the registration form, validations checking for the uniqueness of personal data against current database records must be omitted. If a user attempts to create an account with an email address that already exists in the database, the system should display a success message and, on the backend, simply refrain from creating a new account.&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%2Fkbhz1mayrm02rb00catn.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%2Fkbhz1mayrm02rb00catn.png" alt="User registration form and already registered" width="329" height="188"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;source: &lt;a href="https://ux.stackexchange.com/questions/19834/user-registration-form-and-already-registered" rel="noopener noreferrer"&gt;stackexchange.com&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;For the login form:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;If the account is confirmed and both email and password are correct, return success.&lt;/li&gt;
&lt;li&gt;If the account is not confirmed and both email and password are correct, return success (display unconfirmed email message).&lt;/li&gt;
&lt;li&gt;Otherwise, return &lt;strong&gt;a generic message&lt;/strong&gt; like &lt;code&gt;invalid credentials&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;[IMPORTANT]&lt;/strong&gt; When a user tries to log in with an unconfirmed email but enters an incorrect password, &lt;code&gt;devise_token_auth&lt;/code&gt; gem currently bypasses password validation and directly informs the user that the email is not confirmed. To address this issue, we will &lt;strong&gt;enable paranoid mode in Devise&lt;/strong&gt;. This will ensure that the system only renders the &lt;code&gt;email not confirmed&lt;/code&gt; message if the password is valid. If the password is incorrect, the system will respond with a generic &lt;code&gt;invalid credentials&lt;/code&gt; message.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Ref: &lt;a href="https://github.com/lynndylanhurley/devise_token_auth/blob/master/app/controllers/devise_token_auth/sessions_controller.rb#L20-L43" rel="noopener noreferrer"&gt;devise_token_auth/sessions_controller.rb&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# config/initializers/devise.rb&lt;/span&gt;

&lt;span class="c1"&gt;# It will change confirmation, password recovery and other workflows&lt;/span&gt;
&lt;span class="c1"&gt;# to behave the same regardless if the e-mail provided was right or wrong.&lt;/span&gt;
&lt;span class="c1"&gt;# Does not affect registerable.&lt;/span&gt;
&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;paranoid&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kp"&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&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%2Fi7w4xluzv6ympirkknp7.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%2Fi7w4xluzv6ympirkknp7.png" alt="Password is incorrect" width="575" height="528"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;source: &lt;a href="https://www.techtarget.com/searchsecurity/tip/What-enumeration-attacks-are-and-how-to-prevent-them" rel="noopener noreferrer"&gt;techtarget.com&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;For the reset password form, similar to the registration form, we should not validate the email against current database records to check if it exists. Instead, whenever a user enters an email in a valid format, &lt;strong&gt;we should always return a success message&lt;/strong&gt;. Then, on the backend, we can decide whether to send the email or not. Enabling paranoid mode in Devise also handles this by ensuring that success messages are returned regardless of the email's presence in the database.&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%2Fzji6bmx0kb8jz5js1znv.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%2Fzji6bmx0kb8jz5js1znv.png" alt="Reset password form" width="583" height="282"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;source: &lt;a href="https://meta.discourse.org/t/email-enumeration-vulnerability-on-password-reset-dialogue/273449" rel="noopener noreferrer"&gt;discourse.org&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;To prevent enumeration attacks, &lt;strong&gt;it is crucial to standardize error messages&lt;/strong&gt; so that no information about the validity of usernames or passwords is revealed. By returning generic error messages regardless of which credentials are incorrect, &lt;strong&gt;we reduce the risk of attackers identifying valid usernames and improve the overall security&lt;/strong&gt; of the authentication system. This approach also prevents potential data leaks, safeguarding sensitive user information from being exposed.&lt;/p&gt;




&lt;h3&gt;
  
  
  Timing based attacks ⏱️
&lt;/h3&gt;

&lt;p&gt;The Medium article &lt;a href="https://medium.com/spidernitt/introduction-to-timing-attacks-4e1e8c84b32b" rel="noopener noreferrer"&gt;"Introduction to Timing Attacks!"&lt;/a&gt; explains that '&lt;em&gt;A timing attack is a security exploit that enables an attacker to spot vulnerabilities in a local or a remote system to extract potentially sensitive or secret information by observing the concerned system's response time to various inputs.&lt;/em&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%2Fl9emtfsosvt140bph85x.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%2Fl9emtfsosvt140bph85x.png" alt="Timing based attacks" width="800" height="418"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;source: &lt;a href="https://www.propelauth.com/post/what-does-timing-attack-actually-mean" rel="noopener noreferrer"&gt;propelauth.com&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h4&gt;
  
  
  Prevent timing based enumeration attacks for login forms with the Rack-Attack gem:
&lt;/h4&gt;

&lt;p&gt;To enhance the security of your application and protect against timing based enumeration attacks for login forms, consider implementing throttling constraints for each vulnerable form using the &lt;a href="https://github.com/rack/rack-attack" rel="noopener noreferrer"&gt;Rack-Attack gem&lt;/a&gt;. Rack-Attack is a middleware for Ruby applications that provides a simple way to throttle and block abusive requests.&lt;/p&gt;

&lt;p&gt;This code essentially limits login attempts for a given ip parameter to X (&lt;code&gt;limit&lt;/code&gt;) requests per Y (&lt;code&gt;period&lt;/code&gt;) seconds:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# config/initializers/rack_attack.rb&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Rack::Attack&lt;/span&gt;
  &lt;span class="n"&gt;throttle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"logins/ip"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;limit: &lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;period: &lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;seconds&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
    &lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ip&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;starts_with?&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"/admin/sign_in"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"/api/v1/auth/sign_in"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;post?&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;there is also a throttling option for emails, but it is &lt;strong&gt;important to note that this can lead to DDos attacks&lt;/strong&gt;, as someone can block other users by spamming the login form with their emails. To learn more see here &lt;a href="https://github.com/rack/rack-attack?tab=readme-ov-file#throttling" rel="noopener noreferrer"&gt;Rack-Attack Throttling documentation&lt;/a&gt;.&lt;/p&gt;

&lt;h4&gt;
  
  
  Prevent timing based enumeration attacks for other forms (registration, password reset, etc.):
&lt;/h4&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%2Fcks85ttb8v18xxifoda0.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%2Fcks85ttb8v18xxifoda0.png" alt="Adjusted auth flow" width="800" height="607"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Instead of directly calling the desired &lt;code&gt;use_case&lt;/code&gt;, we can call an indirect &lt;code&gt;use_case&lt;/code&gt; to schedule the requested action.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# app/controllers/api/v1/auth/user_registrations_controller.rb&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;create&lt;/span&gt;
  &lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Auth&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;UseCases&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;ScheduleUserRegistration&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;params: &lt;/span&gt;&lt;span class="n"&gt;create_params&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="c1"&gt;# ...&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We now validate incoming parameters and create a temporary record to store them. This temporary storage layer was introduced to address two key concerns:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Avoiding PII storage in Redis&lt;/strong&gt;&lt;br&gt;
Rather than storing personally identifiable information (PII) - such as registration or password reset form inputs in volatile systems like Redis, we use a dedicated database table designed specifically for temporary records.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Ensuring end-to-end encryption&lt;/strong&gt;&lt;br&gt;
All sensitive data stored in these temporary records is encrypted, maintaining strong security and privacy guarantees throughout the entire lifecycle of the process.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# app/concepts/auth/use_cases/schedule_user_registration.rb&lt;/span&gt;

&lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="nn"&gt;Auth&lt;/span&gt;
  &lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="nn"&gt;UseCases&lt;/span&gt;
    &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ScheduleUserRegistration&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;BaseUseCase&lt;/span&gt;
      &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;:)&lt;/span&gt;
        &lt;span class="c1"&gt;# validate params&lt;/span&gt;
        &lt;span class="n"&gt;reference_record&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;RegistrationRequest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="no"&gt;RegisterUserJob&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;perform_async&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;reference_record&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="k"&gt;end&lt;/span&gt;

      &lt;span class="c1"&gt;# ...&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;By referencing these encrypted temporary records in subsequent steps (e.g., background jobs or finalization use case handlers), &lt;strong&gt;we minimize exposure of sensitive user data and maintain better control over its lifecycle and security posture&lt;/strong&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;  &lt;span class="c1"&gt;# db/migrate/...&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;change&lt;/span&gt;
    &lt;span class="n"&gt;create_table&lt;/span&gt; &lt;span class="ss"&gt;:registration_requests&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;id: :uuid&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
      &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;string&lt;/span&gt; &lt;span class="ss"&gt;:email&lt;/span&gt;
      &lt;span class="c1"&gt;## Database authenticatable&lt;/span&gt;
      &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;string&lt;/span&gt; &lt;span class="ss"&gt;:encrypted_password&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;null: &lt;/span&gt;&lt;span class="kp"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;default: &lt;/span&gt;&lt;span class="s2"&gt;""&lt;/span&gt;

      &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;string&lt;/span&gt; &lt;span class="ss"&gt;:first_name&lt;/span&gt;
      &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;string&lt;/span&gt; &lt;span class="ss"&gt;:last_name&lt;/span&gt;

      &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;timestamps&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# app/models/registration_request.rb&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;RegistrationRequest&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ApplicationRecord&lt;/span&gt;
  &lt;span class="n"&gt;devise&lt;/span&gt; &lt;span class="ss"&gt;:database_authenticatable&lt;/span&gt;

  &lt;span class="n"&gt;encrypts&lt;/span&gt; &lt;span class="ss"&gt;:email&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;deterministic: &lt;/span&gt;&lt;span class="kp"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;downcase: &lt;/span&gt;&lt;span class="kp"&gt;true&lt;/span&gt;
  &lt;span class="n"&gt;encrypts&lt;/span&gt; &lt;span class="ss"&gt;:first_name&lt;/span&gt;
  &lt;span class="c1"&gt;# https://guides.rubyonrails.org/active_record_encryption.html&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the next step, we retrieve the corresponding temporary record and use it to trigger the logic required to process the requested action - such as completing a registration or resetting a password. Once the action is successfully handled, the temporary record is securely deleted, &lt;strong&gt;ensuring that no sensitive data lingers beyond its necessary lifespan&lt;/strong&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# app/sidekiq/register_user_job.rb&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;RegisterUserJob&lt;/span&gt;
  &lt;span class="kp"&gt;include&lt;/span&gt; &lt;span class="no"&gt;Sidekiq&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Job&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;perform&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;registration_request&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;RegistrationRequest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;User&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find_by&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;email: &lt;/span&gt;&lt;span class="n"&gt;registration_request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;email&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;registration_request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;destroy!&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;present?&lt;/span&gt;

    &lt;span class="n"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Auth&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;UseCases&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;RegisterUser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;params: &lt;/span&gt;&lt;span class="n"&gt;registration_request_params&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="no"&gt;Auth&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;UseCases&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;SendConfirmationEmail&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;registration_request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;destroy!&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="c1"&gt;# ...&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now the logic responsible for handling the requested action is as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# app/concepts/auth/use_cases/register_user.rb&lt;/span&gt;

&lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="nn"&gt;Auth&lt;/span&gt;
  &lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="nn"&gt;UseCases&lt;/span&gt;
    &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;RegisterUser&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;BaseUseCase&lt;/span&gt;
      &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;:)&lt;/span&gt;
        &lt;span class="n"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kp"&gt;nil&lt;/span&gt;
        &lt;span class="no"&gt;ActiveRecord&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Base&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;transaction&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
          &lt;span class="n"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;User&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;build&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
          &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;skip_password_validation&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kp"&gt;true&lt;/span&gt;
          &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;save!&lt;/span&gt;
          &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;skip_password_validation&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kp"&gt;false&lt;/span&gt;
        &lt;span class="k"&gt;end&lt;/span&gt;

        &lt;span class="n"&gt;user&lt;/span&gt;
      &lt;span class="k"&gt;end&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As shown in the example, we introduce a slight deviation from standard behavior by leveraging the &lt;code&gt;skip_password_validation&lt;/code&gt; mechanism. &lt;strong&gt;This is necessary when transferring the password&lt;/strong&gt; from a temporary reference record (e.g., &lt;code&gt;RegistrationRequest&lt;/code&gt;) to a persistent user record (&lt;code&gt;User&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;Because &lt;strong&gt;we cannot decrypt the password value&lt;/strong&gt; (for security reasons), we instead copy the encrypted password directly, assigning:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;encrypted_password&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;registration_request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;encrypted_password&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;However, when creating a new User record, Devise (used in Rails) expects a plain-text password to be provided and validated by default. To bypass this requirement without compromising security, we override the &lt;code&gt;password_required?&lt;/code&gt; method to return &lt;code&gt;false&lt;/code&gt; when &lt;strong&gt;using encrypted password values directly&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# app/models/user.rb&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;User&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ApplicationRecord&lt;/span&gt;
  &lt;span class="nb"&gt;attr_accessor&lt;/span&gt; &lt;span class="ss"&gt;:skip_password_validation&lt;/span&gt;  &lt;span class="c1"&gt;# virtual attribute to skip password validation while saving&lt;/span&gt;

  &lt;span class="c1"&gt;# ...&lt;/span&gt;

  &lt;span class="kp"&gt;protected&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;password_required?&lt;/span&gt;
    &lt;span class="k"&gt;super&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;skip_password_validation&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This approach allows the user creation process to proceed securely, using the encrypted password from the temporary record, without triggering unnecessary validation errors.&lt;/p&gt;

&lt;p&gt;To protect against timing based attacks, &lt;strong&gt;it is necessary to implement security measures for various forms&lt;/strong&gt; in the application. Use the Rack-Attack gem to lower the vulnerability to these attacks on login forms.&lt;/p&gt;

&lt;p&gt;For other forms, such as registration or password reset, move the processing logic to background tasks instead of executing them directly. This approach helps hide response times and &lt;strong&gt;prevents attackers from recognizing patterns based on time changes&lt;/strong&gt;, thus increasing the overall security and resilience of the application.&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%2Fm81h48wdx30ucwsmlfst.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%2Fm81h48wdx30ucwsmlfst.png" alt="Timing prevention attacks" width="800" height="418"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;source: &lt;a href="https://www.propelauth.com/post/what-does-timing-attack-actually-mean" rel="noopener noreferrer"&gt;propelauth.com&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;blockquote&gt;
&lt;p&gt;Refs:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://youtu.be/ISPYQvnLW9c?si=QlTw6KQrNp33CoSY" rel="noopener noreferrer"&gt;Devise pitfalls and way to tighten security&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://cheatsheetseries.owasp.org/cheatsheets/Authentication_Cheat_Sheet.html#authentication-and-error-messages" rel="noopener noreferrer"&gt;OWASP - Authentication and Error Messages&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;sup&gt;Cover image source: &lt;a href="https://deepai.org/" rel="noopener noreferrer"&gt;DeepAI&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;

</description>
      <category>programming</category>
      <category>rails</category>
      <category>ruby</category>
      <category>security</category>
    </item>
    <item>
      <title>Must-Know Ruby on Rails Gems for Improved Productivity</title>
      <dc:creator>Patrick Gramatowski</dc:creator>
      <pubDate>Wed, 18 Dec 2024 17:24:39 +0000</pubDate>
      <link>https://dev.to/monterailhub/must-know-ruby-on-rails-gems-for-improved-productivity-3hpa</link>
      <guid>https://dev.to/monterailhub/must-know-ruby-on-rails-gems-for-improved-productivity-3hpa</guid>
      <description>&lt;p&gt;A Ruby on Rails gem is a reusable package of Ruby code that enhances application functionality. Gems provide pre-built solutions for tasks like database integration or handling web requests, saving development time and effort. They streamline projects and enable faster delivery, especially in Ruby on Rails development.&lt;/p&gt;

&lt;p&gt;There are several categories of Ruby on Rails gems:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Performance Optimization&lt;/li&gt;
&lt;li&gt;Security Enhancement&lt;/li&gt;
&lt;li&gt;Productivity and Developer Tools&lt;/li&gt;
&lt;li&gt;Testing and Quality Assurance&lt;/li&gt;
&lt;li&gt;Frontend and UI&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;With so many gems available, choosing the right one can feel overwhelming. Which one should you pick, and why? In the following sections, I'll walk you through the must-know gems.&lt;/p&gt;




&lt;h3&gt;
  
  
  🤔 What are Must-have Ruby on Rails Gems?
&lt;/h3&gt;

&lt;p&gt;Ruby on Rails gems extend the functionality of a Rails application and make development faster, easier, and more efficient.&lt;/p&gt;

&lt;h4&gt;
  
  
  Bundler
&lt;/h4&gt;

&lt;p&gt;The Bundler gem is one of the most popular, with over 2,000,000,000 downloads. It provides a consistent environment for Ruby projects by tracking and installing the exact gems and versions needed. Based on your Gemfile, a single call to Bundler with &lt;code&gt;$ bundle install&lt;/code&gt; will automatically download and install all the required gems.&lt;/p&gt;

&lt;h4&gt;
  
  
  RuboCop
&lt;/h4&gt;

&lt;p&gt;RuboCop is a Ruby code style checking and code formatting tool. It aims to enforce the &lt;a href="https://github.com/rubocop/ruby-style-guide" rel="noopener noreferrer"&gt;community-driven Ruby Style Guide&lt;/a&gt;. In addition to identifying issues in your code, RuboCop can automatically correct many for you. This gem is used widely across Ruby on Rails development services to ensure code quality and maintainability.&lt;/p&gt;

&lt;h4&gt;
  
  
  Bullet
&lt;/h4&gt;

&lt;p&gt;The Bullet Gem helps boost your application's performance by minimizing the number of queries it executes.&lt;br&gt;
It monitors your queries during development and alerts you when to add eager loading to address N+1 queries, when existing eager loading is unnecessary, and when to use counter cache.&lt;/p&gt;

&lt;h3&gt;
  
  
  📝 Summary
&lt;/h3&gt;

&lt;p&gt;Gems significantly speed up the development process, improve code quality, automate repetitive tasks, and enhance application security and performance. The world of gems is tremendous; &lt;a href="https://rubygems.org/stats" rel="noopener noreferrer"&gt;there are more than 183,000 gems&lt;/a&gt; in the latest official Ruby gems base. Whatever gem you choose, you make one step closer to fostering your application development process.&lt;/p&gt;

&lt;h3&gt;
  
  
  🙋 Want to Dive Deeper?
&lt;/h3&gt;

&lt;p&gt;Explore this topic in greater detail in my &lt;a href="https://www.monterail.com/blog/must-have-ruby-on-rails-gems" rel="noopener noreferrer"&gt;blog post&lt;/a&gt; published on &lt;a class="mentioned-user" href="https://dev.to/monterail"&gt;@monterail&lt;/a&gt; blog, where I've covered more Ruby on Rails Gems that help build more performant, secure, and maintainable applications. It is a perfect read for developers looking to enhance their Ruby on Rails applications.&lt;/p&gt;

&lt;p&gt;&lt;sup&gt;Cover image source: &lt;a href="https://lokalise.com/blog/how-to-create-a-ruby-gem-testing-suite/" rel="noopener noreferrer"&gt;Lokalise&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;

</description>
      <category>ruby</category>
      <category>rails</category>
      <category>webdev</category>
      <category>programming</category>
    </item>
    <item>
      <title>How not to let your Tech Lead drown? 🛟</title>
      <dc:creator>Patrick Gramatowski</dc:creator>
      <pubDate>Sun, 01 Dec 2024 11:27:39 +0000</pubDate>
      <link>https://dev.to/rwxpat/how-not-to-let-your-tech-lead-drown-5a96</link>
      <guid>https://dev.to/rwxpat/how-not-to-let-your-tech-lead-drown-5a96</guid>
      <description>&lt;p&gt;I want to start by clarifying that  have no doubt our tech leads are excellent swimmers, but when I was a kid, my mother used to remind me that “&lt;em&gt;you can drown in six inches of water&lt;/em&gt;”. With that in mind, I’d like to share how I strive not to be one of those six inches for the tech leads I work with.&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%2Fyh8jrrg3w9dmts4tleh7.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%2Fyh8jrrg3w9dmts4tleh7.png" alt="baywatch" width="681" height="383"&gt;&lt;/a&gt;&lt;br&gt;
&lt;sup&gt;Image source: &lt;a href="https://deadline.com/2023/04/baywatch-remake-in-the-works-at-fremantle-as-buyers-circle-1235323825/" rel="noopener noreferrer"&gt;‘Baywatch’ Remake In The Works At Fremantle As Buyers Circle&lt;/a&gt; by Peter White&lt;/sup&gt;&lt;/p&gt;




&lt;h3&gt;
  
  
  &lt;strong&gt;Communication flow&lt;/strong&gt; 🤝
&lt;/h3&gt;

&lt;p&gt;When you join a new project, it's time for “onboarding”. This is the point at which you get to know the team members and begin to get familiar with the technical and business logic of the project. After the initial familiarization with the project, the time comes for the first tasks. As you work on your first tasks, you are likely to have questions for the rest of the team, these first interactions lead to the formation of what I call “communication flow”, which I understand as habits in the way you communicate with other team members. As it happens with people, each of us has some habits, and on this point I wanted to encourage you to &lt;strong&gt;start paying attention to your communication habits&lt;/strong&gt; and consciously control them.&lt;/p&gt;

&lt;p&gt;💬 &lt;em&gt;Real-Life Example:&lt;/em&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;While working on one of my previous projects, during one of our retrospective sessions, the project's tech lead pointed out that it makes his job much easier when I leave a reaction emoji under each message, so he can always be sure that the message has been read by me.&lt;/p&gt;
&lt;/blockquote&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%2Fm0d4frqb0ebqe1kql13i.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%2Fm0d4frqb0ebqe1kql13i.png" alt="emojis" width="796" height="375"&gt;&lt;/a&gt;&lt;sup&gt;Image source: &lt;a href="https://www.linkedin.com/pulse/communications-strategies-like-ogres-onions-kim-ronkin-casey-qe4hc/" rel="noopener noreferrer"&gt;How the Evolution of Emojis Changed the Way We Communicate&lt;/a&gt; by Aoife Markey&lt;/sup&gt;&lt;/p&gt;

&lt;p&gt;As you can see, even something as trivial and simple as an emoji can make a difference. 👀&lt;/p&gt;

&lt;p&gt;📚 &lt;em&gt;Explore More:&lt;/em&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;a href="https://slack.com/intl/en-gb/blog/collaboration/asynchronous-communication-best-practices" rel="noopener noreferrer"&gt;Asynchronous communication: Best practices and tips&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h3&gt;
  
  
  &lt;strong&gt;The team 👩‍💻👨‍💻&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;In addition to the tech lead, many other team members are there to support you, including Product Managers (PM), Quality Assurance (QA) specialists, Product Owners (PO), Business Analysts (BAs), and Designers. &lt;strong&gt;Each of these roles plays a vital part in the team's collaborative effort.&lt;/strong&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%2Fmfo7impj3me6t2lw9xd1.jpeg" 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%2Fmfo7impj3me6t2lw9xd1.jpeg" alt="layers-onions" width="768" height="512"&gt;&lt;/a&gt;&lt;sup&gt;Image source: &lt;a href="https://www.linkedin.com/pulse/communications-strategies-like-ogres-onions-kim-ronkin-casey-qe4hc/" rel="noopener noreferrer"&gt;Communications Strategies Are Like Ogres, Like Onions&lt;/a&gt; by Kim Ronkin Casey&lt;/sup&gt;&lt;/p&gt;

&lt;p&gt;Think of these roles as layers of an onion 🧅 — each one represents a team or subgroup that contributes to the overall project. Being mindful of these layers and ensuring effective communication between them is essential for maintaining alignment and avoiding gaps that could lead to delays or misunderstandings.&lt;/p&gt;

&lt;p&gt;📚 &lt;em&gt;Explore More:&lt;/em&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;a href="https://asana.com/resources/team-communication" rel="noopener noreferrer"&gt;How to improve team communication: 6 strategies and tips&lt;/a&gt;&lt;br&gt;
&lt;a href="https://www.monterail.com/blog/best-communication-practices" rel="noopener noreferrer"&gt;Best Communication Practices We Use To Keep Our Clients In The Loop&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h3&gt;
  
  
  &lt;strong&gt;Knowledge sharing (documentation, updates, notes)&lt;/strong&gt; 📄
&lt;/h3&gt;

&lt;p&gt;Documentation is a crucial part of a programmer's work, serving as a key source of knowledge about the project. &lt;strong&gt;It goes beyond code-related details and should also be designed to support non-technical team members&lt;/strong&gt;, such as product owners, project managers, and quality assurance specialists. To ensure that everyone stays on the same page as the project evolves, &lt;strong&gt;every team member should keep an eye on the documentation to keep it updated&lt;/strong&gt; and clearly communicate any changes that may impact others' work.&lt;/p&gt;

&lt;p&gt;💬 &lt;em&gt;Real-Life Example:&lt;/em&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;I made it a habit with every task I work on to include clear instructions on how to test the specific changes, pointing out where technical support might be needed, and specifying where to find the latest changes (e.g. staging). This has become my standard approach, helping reduce confusion and ensuring smoother collaboration throughout the testing and review process.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I believe that clear communication and well-organized documentation are the cornerstones of effective and seamless team collaboration.&lt;/p&gt;

&lt;p&gt;💡 &lt;em&gt;Tip:&lt;/em&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Visuals like diagrams can greatly enhance clarity and improve organization. For more on this, be sure to check out my article - &lt;a href="https://www.monterail.com/blog/how-diagrams-improve-project-documentation" rel="noopener noreferrer"&gt;Visualize to organize: How diagrams improve project documentation&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&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%2Fi90bmxxmbk9d5tq9ott4.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%2Fi90bmxxmbk9d5tq9ott4.png" alt="documentation-not-complicated" width="800" height="985"&gt;&lt;/a&gt;&lt;br&gt;
&lt;sup&gt;Image source: &lt;a href="https://www.reddit.com/r/ProgrammerHumor/comments/11ql3a5/the_comments/" rel="noopener noreferrer"&gt;reddit.com/r/ProgrammerHumor&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;

&lt;p&gt;📚 &lt;em&gt;Explore More:&lt;/em&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;a href="https://diataxis.fr/" rel="noopener noreferrer"&gt;Diátaxis - A systematic approach to technical documentation authoring.&lt;/a&gt;&lt;br&gt;
&lt;a href="https://www.theengineroom.org/library/creating-technical-documentation-for-non-technical-people-tips-from-our-team/" rel="noopener noreferrer"&gt;Creating technical documentation for non-technical people: tips from our team&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h3&gt;
  
  
  &lt;strong&gt;Learn how NOT to make decisions&lt;/strong&gt; 🚦
&lt;/h3&gt;

&lt;p&gt;As a programmer, you will often be faced with situations where you will have to make decisions that can affect an important part of a functionality, the behavior of a specific component, or even the entire project. It is important not to make these decisions in a hurry, without consulting your team.&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%2F327txogw8keh3x5y208m.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%2F327txogw8keh3x5y208m.png" alt="cost-of-delay-1" width="510" height="354"&gt;&lt;/a&gt;&lt;br&gt;
&lt;sup&gt;Image source: &lt;a href="https://www.researchgate.net/publication/329222591_Maersk_Line_-_Accelerators_of_innovation_and_transformation" rel="noopener noreferrer"&gt;The Innovation &amp;amp; Transformation case story of Maersk Line&lt;/a&gt; by Henrik von Scheel &amp;amp; Mads Clausager&lt;/sup&gt;&lt;/p&gt;

&lt;p&gt;When making important decisions, it's essential to think through all the details before acting. &lt;strong&gt;Involving your team in the process can uncover valuable insights, and help spot potential problems or overlooked risks.&lt;/strong&gt; This avoids costly mistakes during product development, such as bugs leading to delays, which in turn can lead to lost sales which directly affects the client's business.&lt;/p&gt;

&lt;p&gt;🚨 &lt;em&gt;Warning:&lt;/em&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;It’s crucial to understand the client's business needs in order to be able to estimate the so-called "&lt;em&gt;Cost of Delay&lt;/em&gt;", since it can significantly affect the client’s outcomes.&lt;/p&gt;
&lt;/blockquote&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%2Fm1r5mx8vil76ha0is0ez.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%2Fm1r5mx8vil76ha0is0ez.png" alt="cost-of-delay-2" width="703" height="242"&gt;&lt;/a&gt;&lt;br&gt;
&lt;sup&gt;Image source: &lt;a href="https://www.researchgate.net/publication/329222591_Maersk_Line_-_Accelerators_of_innovation_and_transformation" rel="noopener noreferrer"&gt;The Innovation &amp;amp; Transformation case story of Maersk Line&lt;/a&gt; by Henrik von Scheel &amp;amp; Mads Clausager&lt;/sup&gt;&lt;/p&gt;

&lt;p&gt;📚 &lt;em&gt;Explore More:&lt;/em&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;a href="https://medium.com/@cmphan7/software-engineer-the-art-of-decision-making-delay-73f013f3d35f" rel="noopener noreferrer"&gt;Software Engineer: The Art of Decision-Making Delay&lt;/a&gt;&lt;br&gt;
&lt;a href="https://becomingagile.wordpress.com/2015/05/25/cost-of-delay/comment-page-1/" rel="noopener noreferrer"&gt;Cost of Delay – Decision Making Framework&lt;/a&gt;&lt;br&gt;
&lt;a href="https://medium.com/good-boss-bad-boss/project-managers-your-new-weekly-client-call-agenda-7a3acc27c472" rel="noopener noreferrer"&gt;Project Managers: Your New Weekly Client Call Agenda&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h3&gt;
  
  
  &lt;strong&gt;Grow&lt;/strong&gt; 🌱
&lt;/h3&gt;

&lt;p&gt;The best way not to drown your tech lead is to become one. Learn, gain experience, actively participate in the life of the project and the company, contribute in the way you feel most comfortable. &lt;strong&gt;The more experience and knowledge you have, the more responsibility you can take on&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;🚀 &lt;em&gt;Ideas:&lt;/em&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;Volunteer for complex or unfamiliar tasks, which will allow you to learn by doing.&lt;/li&gt;
&lt;li&gt;Spend time with more experienced engineers, observing how they solve problems and make decisions.&lt;/li&gt;
&lt;li&gt;Write blog posts or internal documentation about the challenges you faced and how you solved them, following the principle - "&lt;em&gt;if you can't teach/explain it well, you don't understand it well enough&lt;/em&gt;".&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&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%2F5k4d4g7fmrwj4o95jw22.jpeg" 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%2F5k4d4g7fmrwj4o95jw22.jpeg" alt="responsibility" width="400" height="251"&gt;&lt;/a&gt;&lt;sup&gt;Image source: &lt;a href="https://www.linkedin.com/pulse/failure-power-responsibility-ed-berliner/" rel="noopener noreferrer"&gt;The Failure of Power &amp;amp; Responsibility&lt;/a&gt; by Ed Berliner&lt;/sup&gt;&lt;/p&gt;

&lt;p&gt;True development comes from exposing yourself to new experiences, and as you gain knowledge and skills, you naturally take on more and more responsibility.&lt;/p&gt;




&lt;h3&gt;
  
  
  &lt;strong&gt;Conclusions&lt;/strong&gt; 🎯
&lt;/h3&gt;

&lt;p&gt;The purpose of my post was to raise awareness of how important each team member is, and how you, as an individual, can contribute to the effectiveness of the entire team.&lt;/p&gt;

&lt;p&gt;By understanding that you can bring the greatest value to the team, not through your technical skills, but through your communication skills, because before you write the first line of code, you need to know what, how and when it should work. Because we don't write code for code's sake, but to solve problems, and to solve any problem long-term, we have to start by understanding the nature of the problem.&lt;/p&gt;

&lt;p&gt;&lt;sup&gt;Cover image source: &lt;a href="https://deepai.org/" rel="noopener noreferrer"&gt;DeepAI&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;

</description>
      <category>management</category>
      <category>programming</category>
      <category>tutorial</category>
      <category>productivity</category>
    </item>
  </channel>
</rss>
