<?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: Steven Davies</title>
    <description>The latest articles on DEV Community by Steven Davies (@nullabletype).</description>
    <link>https://dev.to/nullabletype</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%2F701664%2F3062b681-2626-44b2-b72e-9b5b8932e47b.png</url>
      <title>DEV Community: Steven Davies</title>
      <link>https://dev.to/nullabletype</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/nullabletype"/>
    <language>en</language>
    <item>
      <title>Proxy any class by interface in C# with DispatchProxy</title>
      <dc:creator>Steven Davies</dc:creator>
      <pubDate>Fri, 22 Oct 2021 22:27:23 +0000</pubDate>
      <link>https://dev.to/nullabletype/proxy-any-class-by-interface-in-c-with-dispatchproxy-2i6a</link>
      <guid>https://dev.to/nullabletype/proxy-any-class-by-interface-in-c-with-dispatchproxy-2i6a</guid>
      <description>&lt;p&gt;I was recently tasked with figuring out a way to performance test database calls being made via &lt;code&gt;NHibernate&lt;/code&gt; to &lt;code&gt;PostgreSQL&lt;/code&gt;. As far as I could find, &lt;code&gt;NHibernate&lt;/code&gt; doesn't expose any really easy ways to intercept and manually log both the &lt;code&gt;SQL&lt;/code&gt; and execution times for each query. &lt;/p&gt;

&lt;p&gt;After a little more digging, I came across a handy lesser-known tool that allows you to easily proxy any interface, be it user-defined or from a third party library. This tool lives in the &lt;a href="https://www.nuget.org/packages?q=System.Reflection.DispatchProxy"&gt;&lt;code&gt;System.Reflection.DispatchProxy&lt;/code&gt;&lt;/a&gt; nuget package. With a little code, you can wrap any instance of a class that implements an interface with a proxy to not only log but also manipulate both arguments and returned data.&lt;/p&gt;

&lt;h2&gt;
  
  
  Example
&lt;/h2&gt;

&lt;p&gt;Take this simple class and interface.&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="k"&gt;interface&lt;/span&gt; &lt;span class="nc"&gt;IHello&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="nf"&gt;SayHello&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;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;class&lt;/span&gt; &lt;span class="nc"&gt;Hello&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;IHello&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="nf"&gt;SayHello&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;name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;$"Hello &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="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;true&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;p&gt;Nothing special, only one method exposed which takes a simple name parameter, writes out a string to the console and returns a bool indicating success.&lt;/p&gt;

&lt;p&gt;Say, although contrived in this example, we wanted to record calls going to &lt;code&gt;SayHello&lt;/code&gt;.&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="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;HelloDispatchProxy&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;DispatchProxy&lt;/span&gt; &lt;span class="k"&gt;where&lt;/span&gt; &lt;span class="n"&gt;T&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;IHello&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="n"&gt;IHello&lt;/span&gt; &lt;span class="n"&gt;Target&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;protected&lt;/span&gt; &lt;span class="k"&gt;override&lt;/span&gt; &lt;span class="kt"&gt;object&lt;/span&gt; &lt;span class="nf"&gt;Invoke&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;MethodInfo&lt;/span&gt; &lt;span class="n"&gt;targetMethod&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;object&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Code here to track time, log call etc.&lt;/span&gt;
        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;targetMethod&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Invoke&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Target&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;args&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;result&lt;/span&gt;&lt;span class="p"&gt;;&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;static&lt;/span&gt; &lt;span class="n"&gt;T&lt;/span&gt; &lt;span class="nf"&gt;CreateProxy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt; &lt;span class="n"&gt;target&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;proxy&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Create&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;HelloDispatchProxy&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;gt;()&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;HelloDispatchProxy&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;;&lt;/span&gt;
        &lt;span class="n"&gt;proxy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Target&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;target&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;proxy&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;T&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;p&gt;This is our &lt;code&gt;DispatchProxy&lt;/code&gt;, &lt;code&gt;HelloDispatchProxy&lt;/code&gt;. Usage is pretty simple.&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="n"&gt;IHello&lt;/span&gt; &lt;span class="n"&gt;hello&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;HelloDispatchProxy&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;IHello&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;.&lt;/span&gt;&lt;span class="nf"&gt;CreateProxy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;Hello&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;CreateProxy&lt;/code&gt; method calls &lt;code&gt;Create&amp;lt;T&amp;gt;&lt;/code&gt; on the base abstract class to generate a proxy object for you. The target instance is then assigned to the &lt;code&gt;Target&lt;/code&gt; property before returning the proxy cast to type &lt;code&gt;T&lt;/code&gt; (&lt;code&gt;IHello&lt;/code&gt; in this instance).&lt;/p&gt;

&lt;p&gt;Any method that is then called on the proxied &lt;code&gt;IHello&lt;/code&gt; instance will trigger &lt;code&gt;Invoke()&lt;/code&gt;. It's your proxies responsibility to call the target method, pass through the parameters and return the result. In this method, you can do whatever you like, and you'll also have access to your original &lt;code&gt;Target&lt;/code&gt; instance.&lt;/p&gt;

&lt;p&gt;You can see a complete example of this over on my &lt;a href="https://github.com/nullabletype/scratchpad/blob/main/DispatchProxyExample/DispatchProxyExample/HelloDispatchProxy.cs"&gt;GitHub&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;With great power comes great responsibility&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This allows you quite a lot of control, so be careful what you do with &lt;code&gt;args&lt;/code&gt; and the returned result or you can cause some unexpected behaviour.&lt;/p&gt;

&lt;h2&gt;
  
  
  Performance testing
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;DispatchProxy&lt;/code&gt; lives under the &lt;code&gt;System.Reflection&lt;/code&gt; namespace. Because of this, I was curious about what overhead is introduced by this approach. What better way to understand than to test it myself.&lt;/p&gt;

&lt;p&gt;I set up a &lt;a href="https://github.com/nullabletype/scratchpad/blob/main/DispatchProxyExample/DispatchProxyExample/Program.cs"&gt;slightly over-engineered set of tests&lt;/a&gt; to give a raw comparison between calls to both an unproxied and proxied instance. I ran these multiple times to get an average before analysing the results. &lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    100000 W/O  100000 W    % Increase
    2233.023    2382.8835   6.71%
    2235.5306   2398.1216   7.27%
    2236.2449   2386.4416   6.72%
    2236.8529   2389.209    6.81%
    2239.9692   2402.4627   7.25%
    2227.0108   2367.0306   6.29%
    2237.8432   2384.8429   6.57%
    2233.5528   2392.8394   7.13%
    2238.411    2398.3124   7.14%
    2235.7398   2388.134    6.82%
    2262.3454   2380.0595   5.20%
    2340.0243   2385.0553   1.92%
    2249.3693   2383.6925   5.97%
    2246.9835   2381.507    5.99%
    2240.7711   2381.4675   6.28%
    2232.5296   2374.0465   6.34%
    2248.0095   2359.8636   4.98%
    2250.2518   2394.8422   6.43%
    2234.7757   2387.8114   6.85%
    2238.7645   2387.8047   6.66%


    0.00112245  0.001192661 6.27%
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;So on average, the execution took an extra 6 or so percent with just the introduction of the proxy. Bear in mind in this current state, the proxy is doing nothing useful at all, but this helps put into context the efficiency.&lt;/p&gt;

&lt;p&gt;But if you were to add something slow like a blocking console write line in the proxy, the % increase shoots up from 6.27% to over 100%.&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="n"&gt;System&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;$"Going to call &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;targetMethod&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="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The most important part with the performance impact is what you decide to do in the &lt;code&gt;Invoke&lt;/code&gt; method, something you'll need to benchmark yourself.&lt;/p&gt;

&lt;p&gt;In all but the highest performance-critical applications, the overhead of the proxy itself is minor. If you take for example proxying 2-3 DB calls in the context of something like a HTTP web request or message handler the impact is negligible. In fact, in my testing with a real-world application, the numbers are within an acceptable margin of error even when tracing into an &lt;a href="https://en.wikipedia.org/wiki/Application_performance_management"&gt;APM&lt;/a&gt; tool.&lt;/p&gt;

&lt;h2&gt;
  
  
  NHibernate &amp;amp; PostgreSQL
&lt;/h2&gt;

&lt;p&gt;If you're specifically interested in collecting information from &lt;code&gt;NHibernate&lt;/code&gt; as I was, you can do this by wrapping the &lt;code&gt;IDBCommand&lt;/code&gt; in a custom driver implementation.&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="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ProfiledNpgsqlDriver&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;NpgsqlDriver&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;override&lt;/span&gt; &lt;span class="n"&gt;IDbCommand&lt;/span&gt; &lt;span class="nf"&gt;CreateCommand&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;command&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;base&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;CreateCommand&lt;/span&gt;&lt;span class="p"&gt;();&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;ConfigurationManager&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AppSettings&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"EnableDBTracing"&lt;/span&gt;&lt;span class="p"&gt;]?.&lt;/span&gt;&lt;span class="nf"&gt;ToLower&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="s"&gt;"true"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;command&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ProfiledDbCommandDispatchProxy&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;IDbCommand&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;.&lt;/span&gt;&lt;span class="nf"&gt;CreateProxy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;command&lt;/span&gt;&lt;span class="p"&gt;);&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;command&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;p&gt;You can access the command being executed from the target &lt;code&gt;IDBCommand&lt;/code&gt;'s &lt;code&gt;Command&lt;/code&gt; property and record time etc. with a &lt;code&gt;StopWatch&lt;/code&gt;. I went a step further and implemented the proxy as an &lt;a href="https://docs.microsoft.com/en-us/dotnet/api/system.iobservable-1?view=net-5.0"&gt;&lt;code&gt;IObservable&amp;lt;T&amp;gt;&lt;/code&gt;&lt;/a&gt; so I can handle it differently depending on the use case.&lt;/p&gt;

&lt;h2&gt;
  
  
  Wrap up
&lt;/h2&gt;

&lt;p&gt;Hopefully, you found this interesting. Can you think of any good use cases for this, or have any comments? Would love to hear them ❤&lt;/p&gt;

</description>
      <category>csharp</category>
      <category>proxy</category>
      <category>dotnet</category>
      <category>programming</category>
    </item>
    <item>
      <title>Auto OpenVPN reconnect and killswitch for VPNs with dynamic IPs</title>
      <dc:creator>Steven Davies</dc:creator>
      <pubDate>Sat, 02 Oct 2021 22:22:32 +0000</pubDate>
      <link>https://dev.to/nullabletype/auto-openvpn-reconnect-and-killswitch-for-vpns-with-dynamic-ips-4fnm</link>
      <guid>https://dev.to/nullabletype/auto-openvpn-reconnect-and-killswitch-for-vpns-with-dynamic-ips-4fnm</guid>
      <description>&lt;h3&gt;
  
  
  The Problem
&lt;/h3&gt;

&lt;p&gt;If you’re like me and run a home Linux server, you may wish to add some extra security in the form of a VPN connection to ensure traffic is encrypted. In my case, I use a Raspberry Pi to backup things like RAW images from my camera to an off-site location. The extra security gives me a little peace of mind, especially as the server generally uses a cellular connection.&lt;/p&gt;

&lt;p&gt;I’ve been using &lt;code&gt;UFW&lt;/code&gt; to work as a killswitch and only allow traffic via the VPN connection, but one issue I've had is with my trusted VPN provider is that they regularly rotate IP addresses. &lt;code&gt;UFW&lt;/code&gt; uses &lt;code&gt;iptables&lt;/code&gt; under the hood, meaning it can only be configured to use IP addresses explicitly and not a domain name. This has left my backup machine without an internet connection fairly frequently.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Solution
&lt;/h3&gt;

&lt;p&gt;Just to set this straight from the start, I’m no UNIX genius. I spend most of my time working with Windows machines and use Linux more as a hobbyist. That been said, I’ve written my fair share of code/scripts.&lt;/p&gt;

&lt;p&gt;My solution is pretty simple; when disconnects happen:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; fetch a fresh list of IP’s from my internal DNS server.&lt;/li&gt;
&lt;li&gt; Update &lt;code&gt;UFW&lt;/code&gt; .&lt;/li&gt;
&lt;li&gt; Update the &lt;code&gt;OpenVPN&lt;/code&gt;connection file and reconnect.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;I thought I’d share my approach, as I failed finding any other solution online so this may be of help to others.&lt;/p&gt;

&lt;p&gt;The first part is fetching the list of domains. To do this, I use &lt;a href="https://downloads.isc.org/isc/bind9/cur/9.17/doc/arm/html/manpages.html#dig-dns-lookup-utility"&gt;dig&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;ipoutput&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;dig &lt;span class="nv"&gt;$1&lt;/span&gt; +short&lt;span class="si"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nt"&gt;-z&lt;/span&gt; “&lt;span class="nv"&gt;$ipoutput&lt;/span&gt;” &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then  
  &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; “No ips found &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="nv"&gt;$1&lt;/span&gt;”  
  &lt;span class="nb"&gt;exit &lt;/span&gt;1  
&lt;span class="k"&gt;fi

&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; “Found ips &lt;span class="nv"&gt;$ipoutput&lt;/span&gt;”

readarray &lt;span class="nt"&gt;-t&lt;/span&gt; ips &lt;span class="o"&gt;&amp;lt;&amp;lt;&amp;lt;&lt;/span&gt;”&lt;span class="nv"&gt;$ipoutput&lt;/span&gt;”
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This gives me a nice list of IP address for the specified domain.&lt;/p&gt;

&lt;p&gt;Now I know the correct ip addresses, I need to make use of them. The first job is to update &lt;code&gt;UFW&lt;/code&gt;. I decided to use a template file so I can set it up as I want for each machine. This may not be the most elegant solution, but its pretty flexible.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;ufwoutput&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;’’

&lt;span class="k"&gt;for &lt;/span&gt;i &lt;span class="k"&gt;in&lt;/span&gt; “&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;ips&lt;/span&gt;&lt;span class="p"&gt;[@]&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;”  
&lt;span class="k"&gt;do&lt;/span&gt;  
  :  
  ufwoutput+&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$”&lt;/span&gt;&lt;span class="c"&gt;### tuple ### allow udp any $i any 0.0.0.0\\/0 out\\n”  &lt;/span&gt;
  ufwoutput+&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$”&lt;/span&gt;&lt;span class="nt"&gt;-A&lt;/span&gt; ufw-user-output &lt;span class="nt"&gt;-p&lt;/span&gt; udp &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="nv"&gt;$i&lt;/span&gt; &lt;span class="nt"&gt;-j&lt;/span&gt; ACCEPT&lt;span class="se"&gt;\\&lt;/span&gt;n&lt;span class="se"&gt;\\&lt;/span&gt;n”  
&lt;span class="k"&gt;done

if &lt;/span&gt;&lt;span class="nb"&gt;test&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; “&lt;span class="nv"&gt;$dir_path&lt;/span&gt;/user.rules.tmp”&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then  
  &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; “Deleting tmp file…”  
  &lt;span class="nb"&gt;rm&lt;/span&gt; “&lt;span class="nv"&gt;$dir_path&lt;/span&gt;/user.rules.tmp”  
&lt;span class="k"&gt;fi

&lt;/span&gt;&lt;span class="nb"&gt;cp&lt;/span&gt; “&lt;span class="nv"&gt;$dir_path&lt;/span&gt;/user.rules.template” “&lt;span class="nv"&gt;$dir_path&lt;/span&gt;/user.rules.tmp”

&lt;span class="nb"&gt;sed&lt;/span&gt; &lt;span class="nt"&gt;-i&lt;/span&gt; &lt;span class="nt"&gt;-e&lt;/span&gt; “s/&lt;span class="se"&gt;\[&lt;/span&gt;content&lt;span class="se"&gt;\]&lt;/span&gt;/&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;ufwoutput&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;/g” “&lt;span class="nv"&gt;$dir_path&lt;/span&gt;/user.rules.tmp” “&lt;span class="nv"&gt;$dir_path&lt;/span&gt;/user.rules.tmp”

&lt;span class="nb"&gt;mv&lt;/span&gt; “&lt;span class="nv"&gt;$dir_path&lt;/span&gt;/user.rules.tmp” /etc/ufw/user.rules

&lt;span class="nb"&gt;echo&lt;/span&gt; “Reloading ufw”  
ufw reload
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The script builds up a string containing the correct formatted lines for the &lt;code&gt;user.rules&lt;/code&gt; file for &lt;code&gt;UFW&lt;/code&gt;, creates a new copy of the template, replaces &lt;code&gt;[content]&lt;/code&gt; in the file using &lt;code&gt;sed&lt;/code&gt; then moves the file into the correct place (for ubuntu). The final thing to do is &lt;code&gt;ufw reload&lt;/code&gt; to reload the &lt;code&gt;user.rules&lt;/code&gt; file.&lt;/p&gt;

&lt;p&gt;That’s &lt;code&gt;UFW&lt;/code&gt; taken care of, time for &lt;code&gt;OpenVPN&lt;/code&gt; using a similar method&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;openvpnoutput&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;’’

&lt;span class="k"&gt;for &lt;/span&gt;i &lt;span class="k"&gt;in&lt;/span&gt; “&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;ips&lt;/span&gt;&lt;span class="p"&gt;[@]&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;”  
&lt;span class="k"&gt;do&lt;/span&gt;  
:  
  openvpnoutput+&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$”&lt;/span&gt;remote &lt;span class="nv"&gt;$i&lt;/span&gt; 1198&lt;span class="se"&gt;\\&lt;/span&gt;n”  
&lt;span class="k"&gt;done

if &lt;/span&gt;&lt;span class="nb"&gt;test&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; “&lt;span class="nv"&gt;$dir_path&lt;/span&gt;/openvpn.ovpn.tmp”&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then  
  &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; “Deleting tmp file…”  
  &lt;span class="nb"&gt;rm&lt;/span&gt; “&lt;span class="nv"&gt;$dir_path&lt;/span&gt;/openvpn.ovpn.tmp”  
&lt;span class="k"&gt;fi

&lt;/span&gt;&lt;span class="nb"&gt;cp&lt;/span&gt; “&lt;span class="nv"&gt;$dir_path&lt;/span&gt;/openvpn.ovpn.template” “&lt;span class="nv"&gt;$dir_path&lt;/span&gt;/openvpn.ovpn.tmp”

&lt;span class="nb"&gt;sed&lt;/span&gt; &lt;span class="nt"&gt;-i&lt;/span&gt; &lt;span class="nt"&gt;-e&lt;/span&gt; “s/&lt;span class="se"&gt;\[&lt;/span&gt;content&lt;span class="se"&gt;\]&lt;/span&gt;/&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;openvpnoutput&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;/g” “&lt;span class="nv"&gt;$dir_path&lt;/span&gt;/openvpn.ovpn.tmp” “&lt;span class="nv"&gt;$dir_path&lt;/span&gt;/openvpn.ovpn.tmp”

&lt;span class="nb"&gt;mv&lt;/span&gt; “&lt;span class="nv"&gt;$dir_path&lt;/span&gt;/openvpn.ovpn.tmp” “openvpn.ovpn”
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This works in a similar way to the &lt;code&gt;UFW&lt;/code&gt; code, replacing a &lt;code&gt;[content]&lt;/code&gt; placeholder in the &lt;code&gt;.opvn&lt;/code&gt; template file.&lt;/p&gt;

&lt;p&gt;So that’s all the code to update &lt;code&gt;UFW&lt;/code&gt; and &lt;code&gt;OpenVPN&lt;/code&gt;. The next thing to do is to manage the connection. I do this via a simple script called from a &lt;code&gt;cron&lt;/code&gt; job.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="k"&gt;function &lt;/span&gt;getStatus &lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;  
  &lt;span class="nb"&gt;echo&lt;/span&gt; “Attempting to get device status…”  
  ip address show | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nv"&gt;$1&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="k"&gt;return &lt;/span&gt;1  
  &lt;span class="k"&gt;return &lt;/span&gt;0  
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is a pretty straightforward function. &lt;code&gt;grep&lt;/code&gt; the output from &lt;code&gt;ip address show&lt;/code&gt; to see if the &lt;code&gt;OpenVPN&lt;/code&gt; connection is live. Return either &lt;code&gt;1&lt;/code&gt;for success or &lt;code&gt;0&lt;/code&gt;for failure.&lt;/p&gt;

&lt;p&gt;The last thing to do is to check the result and connect if required.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;getStatus &lt;span class="nv"&gt;$device&lt;/span&gt;  
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[[&lt;/span&gt; &lt;span class="nv"&gt;$?&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; 0 &lt;span class="o"&gt;]]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then  
  &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; “openvpn is not connected!”  
  &lt;span class="nb"&gt;echo&lt;/span&gt; “Reconnecting!”

  &lt;span class="c"&gt;#Update config  &lt;/span&gt;
  updateIps &lt;span class="nv"&gt;$domain&lt;/span&gt; &amp;amp;&amp;gt; &lt;span class="nv"&gt;$dir_path&lt;/span&gt;/renew.out &amp;amp;disown

  openvpn — config “&lt;span class="nv"&gt;$dir_path&lt;/span&gt;/openvpn.ovpn” &amp;amp;&amp;gt;&lt;span class="nv"&gt;$dir_path&lt;/span&gt;/out.out &amp;amp;disown  
&lt;span class="k"&gt;else  
  &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; “openvpn is connected!”  
&lt;span class="k"&gt;fi&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the output from &lt;code&gt;getStatus&lt;/code&gt; is &lt;code&gt;0&lt;/code&gt;, then it will call to update the IP addresses and trigger a new &lt;code&gt;OpenVPN&lt;/code&gt; connection with &lt;code&gt;&amp;amp;disown&lt;/code&gt; to not wait for the connection to exit.&lt;/p&gt;

&lt;p&gt;Now that’s all in place, all that’s needed to run the script automatically is a &lt;code&gt;cron&lt;/code&gt; job to check as frequently as you wish.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="k"&gt;*&lt;/span&gt;/1 &lt;span class="k"&gt;*&lt;/span&gt; &lt;span class="k"&gt;*&lt;/span&gt; &lt;span class="k"&gt;*&lt;/span&gt; &lt;span class="k"&gt;*&lt;/span&gt; &lt;span class="nb"&gt;cd&lt;/span&gt; /home/account/openvpn &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; ./auto-connect.sh “my.vpn.domain” “tun0” &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; out.out
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If this is useful to you, rather than having to copy &amp;amp; paste the code I’ve wrapped all this up into a &lt;a href="https://github.com/nullabletype/openvpntools"&gt;single script available on GitHub&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Wrap Up
&lt;/h3&gt;

&lt;p&gt;As I say, I’m no Linux sysadmin but this script solves a problem for me and has been working well for the last few months. If anyone has any suggestions for improvements or comments I’d love to hear them. Even better, &lt;a href="https://github.com/nullabletype/openvpntools"&gt;fork the repo on GitHub&lt;/a&gt; and pop in a merge request ❤&lt;/p&gt;

</description>
      <category>openvpn</category>
      <category>vpn</category>
      <category>security</category>
      <category>linux</category>
    </item>
    <item>
      <title>Thoughts on fine tuning your tech CV</title>
      <dc:creator>Steven Davies</dc:creator>
      <pubDate>Sat, 02 Oct 2021 21:53:16 +0000</pubDate>
      <link>https://dev.to/nullabletype/thoughts-on-fine-tuning-your-tech-cv-1f3l</link>
      <guid>https://dev.to/nullabletype/thoughts-on-fine-tuning-your-tech-cv-1f3l</guid>
      <description>&lt;p&gt;It's almost cruel that your first step into a potential new role often falls to a few A4 sides attempting to summarise yourself; your skills, your drive, your goals. You're more than that can cover. The person reviewing your CV knows that, but they face a difficult decision on whether to invest the time to get to know you based on this first introduction.&lt;/p&gt;

&lt;p&gt;I've been responsible for hiring into multiple technical positions over the years across different skill areas including developers, testers and sys ops engineers. Some of these have been recent graduates, others looking for senior positions after decades in the industry. &lt;/p&gt;

&lt;p&gt;I've seen a wide array of CV styles, some that have delighted me and some that have been difficult to navigate. I thought it may be useful to someone out there, or maybe just myself, to list out some of the suggestions I can think of to help make your tech CV easier to digest. These are all based on real examples I've seen more than once.&lt;/p&gt;

&lt;h2&gt;
  
  
  Consider your audience
&lt;/h2&gt;

&lt;p&gt;This isn't just for your CV's, this is for everything. Whether you're applying for a new position, putting in a merge request to your peers or writing a best man's speech... it's important to put yourself in the shoes of the person you're writing for. What do they want to know?&lt;/p&gt;

&lt;p&gt;Regarding CV's, imagine you're in the position of hiring for the job &lt;strong&gt;you&lt;/strong&gt; want. What would you want to know, what information matters and just as importantly what information doesn't?&lt;/p&gt;

&lt;h2&gt;
  
  
  The opener
&lt;/h2&gt;

&lt;p&gt;Although it is unlikely to be the first section that is read, a personal statement is an opportunity to impress and set a clear indication about what you're looking for and have achieved. If well written, it can easily help you stand out from other candidates.&lt;/p&gt;

&lt;p&gt;One of the biggest traps I've seen people fall into is to state the basic cliches. I'm talking about things like "hard-working" or "dedicated", they're really expected traits and don't need to be said. Space on your CV is precious, and I'm sure there are things that you're proud of you want to tell the reader about over basic statements.&lt;/p&gt;

&lt;p&gt;The second thing I often see is an overuse of vocabulary. You don't need to impress with fancy words, do so by saying what you've achieved in a clear and easy to read manner.&lt;/p&gt;

&lt;p&gt;Pick out the key accomplishments and skills you want the reader to know about you, what the position you're looking for is and the direction of your career. Be honest, you don't want to lie and get an interview for a position you don't want, but feel free to tailor it to the position you're applying to. Keep the fancy words to a minimum, but don't be afraid to be confident about what you're good at. &lt;/p&gt;

&lt;p&gt;Here's an example of what I'd consider a good personal statement.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;An &lt;strong&gt;ISTQB certified&lt;/strong&gt; software developer in test with &lt;strong&gt;8 years' experience&lt;/strong&gt; testing enterprise level web applications. &lt;strong&gt;Designed, written and maintained frameworks&lt;/strong&gt; that ensure a &lt;strong&gt;high level of quality and resilience&lt;/strong&gt;, including testing web API's and applications for functionality and &lt;strong&gt;performance&lt;/strong&gt; that have &lt;strong&gt;reduced required testing time by 50%&lt;/strong&gt;. Thrives on the opportunity to &lt;strong&gt;mentor other testers&lt;/strong&gt;, work collaboratively in a team and has a keen eye for spotting opportunities for improvement. Now looking for the next challenge to use the skills gained to make a difference and continue professional growth.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Size matters
&lt;/h2&gt;

&lt;p&gt;If your CV is jam-packed with information covering 10+ pages, then it's going to be difficult to digest and pick out the relevant information you want the reader to see.&lt;/p&gt;

&lt;p&gt;Similarly, if your CV is a page or less, you're likely omitting really useful information about yourself and your skills. &lt;/p&gt;

&lt;p&gt;The best CV's I've personally seen are generally around 1-3 pages long. Any longer than that then you may benefit from having a tidy.&lt;/p&gt;

&lt;h3&gt;
  
  
  So how do I make the best use of the space?
&lt;/h3&gt;

&lt;h4&gt;
  
  
  Keep it relevant
&lt;/h4&gt;

&lt;p&gt;The IT sector runs at a super-fast pace. Your most recent positions are always likely to be the most relevant on your career path. Take a look at your previous employment. That job you did 5+ years ago using some obscure or out of favour tech... ask yourself, do I want to even do that thing again? Is it relevant to this position or where I want my career to go? &lt;/p&gt;

&lt;p&gt;The further back your experience the less specific you probably need to be. Don't omit previous positions altogether, but you can afford more brevity the further back you go. One size doesn't fit all, just use the space wisely.&lt;/p&gt;

&lt;h4&gt;
  
  
  Summary of tech skills
&lt;/h4&gt;

&lt;p&gt;We use so many tools, so many processes and workflows... you want the person reviewing your CV to see all the wonderful things you know and have used. This can quickly turn into 1/4 of a page of your CV or a giant list of unsorted tools and skills. &lt;/p&gt;

&lt;p&gt;The problem is if you're not careful you can really hurt its usefulness. Take a look at the following example:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;C#, Visual studio, MVC, WebApi, Postgresql, MongoDB, T-SQL, Flask, SQLite, LiteDB, Entity Framework, PostgreSQL, SQL Server, AWS, Python, Microsoft Word, Fiddler, Postman...&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The first issue is that there are quite a few things that are not relevant and just distract from what you're actually good at. Things like listing Visual studio along with C# when that's pretty much implied or listing office tools which is an expectation for any IT job. There may also be things you have no real interest in doing again, so think if it's worth listing them. Some may just be completely irrelevant for the position you're applying for.&lt;/p&gt;

&lt;p&gt;The second issue is that it doesn't really give any idea of your experience. I've seen lots of CV tip sites that recommend a list of years of experience but that doesn't always help. Take for example C#. Is that building desktop apps, or web APIs? Maybe it's all in windows services or WCF.&lt;/p&gt;

&lt;p&gt;My current suggestion would be something like the following, but I'd love to hear/see your ideas 🙂&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Type&lt;/th&gt;
&lt;th&gt;Technologies&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Core Languages&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;C#&lt;/strong&gt; 7+, Asp.Net 4+ (WebApi &amp;amp; MVC), Entity framework&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Core Databases&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;PostgreSQL&lt;/strong&gt;, &lt;strong&gt;MongoDB&lt;/strong&gt;, &lt;strong&gt;T-SQL&lt;/strong&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Core Tooling&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;OctopusDeploy&lt;/strong&gt;, &lt;strong&gt;TeamCity&lt;/strong&gt;, &lt;strong&gt;Fiddler&lt;/strong&gt;, &lt;strong&gt;Postman&lt;/strong&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Other&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Python&lt;/strong&gt;, Flask, &lt;strong&gt;Ms SQL Server&lt;/strong&gt;, &lt;strong&gt;SQLite&lt;/strong&gt;, &lt;strong&gt;LiteDB&lt;/strong&gt;, &lt;strong&gt;AWS&lt;/strong&gt; (EC2, X-Ray S3)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;It's always a good idea to link any experience you mention in the summary with your job roles and include context, like:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;In this role, I was a &lt;strong&gt;key architect&lt;/strong&gt; in developing &lt;strong&gt;large scale&lt;/strong&gt; C# (&lt;strong&gt;8.0, .net 4.7.2&lt;/strong&gt;) apps with PostgreSQL and entity framework.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  The design
&lt;/h2&gt;

&lt;p&gt;This doesn't apply to all roles as it may be useful for a graphic designer position for example but if you're applying for a developer/tester/sys ops type role don't focus too much on the design. Stand out by your clear and obvious thought when preparing your CV by keeping the content clear and easy to read. You don't need colours, graphics or charts to sell yourself, they're just a distraction. I'm not saying make it ugly, just don't overthink it.&lt;/p&gt;

&lt;h2&gt;
  
  
  The bonus
&lt;/h2&gt;

&lt;p&gt;You're in this industry (hopefully) because you love tech. You probably have personal projects you work on outside of your normal job. It's always a real bonus to get to see what candidates have worked on in their spare time, throw that GitHub or personal website link in there or in your covering letter.&lt;/p&gt;

&lt;p&gt;So that's it, I hope you found some of it useful. Some of the points may seem a little obvious, but they're all based on things I've seen frequently.&lt;/p&gt;

&lt;p&gt;I'd love to know if you agree with any of these thoughts, or maybe you have some of your own tips and tricks you'd like to share? ♥&lt;/p&gt;

</description>
      <category>resume</category>
      <category>career</category>
      <category>interview</category>
      <category>cv</category>
    </item>
  </channel>
</rss>
