<?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: Liam Hammett</title>
    <description>The latest articles on DEV Community by Liam Hammett (@liamhammett).</description>
    <link>https://dev.to/liamhammett</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%2F100807%2Fe69e19d0-da23-4ddf-bfe2-6914f7cfec8b.jpeg</url>
      <title>DEV Community: Liam Hammett</title>
      <link>https://dev.to/liamhammett</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/liamhammett"/>
    <language>en</language>
    <item>
      <title>A Look At PHP's isset()</title>
      <dc:creator>Liam Hammett</dc:creator>
      <pubDate>Sat, 08 Jun 2019 16:29:15 +0000</pubDate>
      <link>https://dev.to/liamhammett/a-look-at-php-s-isset-2bc6</link>
      <guid>https://dev.to/liamhammett/a-look-at-php-s-isset-2bc6</guid>
      <description>&lt;p&gt;&lt;a href="https://www.php.net/manual/en/function.isset.php"&gt;&lt;code&gt;isset()&lt;/code&gt;&lt;/a&gt; is one of the most important tools at your disposal to validate data in PHP. Like the name implies, it is designed to verify if a variable given to it is set, returning a boolean value based on the result.&lt;/p&gt;

&lt;p&gt;However, it has some quirks and behaviours that are very much worth knowing as they can easily catch out even experienced developers.&lt;/p&gt;

&lt;p&gt;Let's take a look through how it behaves and what's so special about it. Even if you're a veteran PHP developer, hopefully, you'll pick up something new here.&lt;/p&gt;

&lt;h1&gt;
  
  
  It's a language construct, NOT a function
&lt;/h1&gt;

&lt;p&gt;Despite looking like one, &lt;code&gt;isset()&lt;/code&gt; is actually NOT a typical function as you might expect.&lt;/p&gt;

&lt;p&gt;Just like die(), array(), print() and others, it is a "language construct", which is a fancy term that, in laymen's terms, means it's built directly into the PHP engine and can have some special behaviour that is different than typical built-in or user-defined functions, which we will go over next.&lt;/p&gt;

&lt;h1&gt;
  
  
  It can not be used as a callable
&lt;/h1&gt;

&lt;p&gt;Any built-in or user-defined function can be used as a "callable" function pointer to be invoked dynamically and used for patterns like currying.&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;is_callable('strtoupper');
// true

array_map('strtoupper', ['a', 'b', null, 'd']);
// ['A', 'B', '', 'D']
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;As it is a language construct and not really a function, it is not callable and cannot be used in such a way.&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;is_callable('isset');
// false

array_map('isset', ['a', 'b', null, 'd']);
// PHP Warning:  array_map() expects parameter 1 to be a valid callback, function 'isset' not found or invalid function name…
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h1&gt;
  
  
  It does not accept an expression
&lt;/h1&gt;

&lt;p&gt;While regular functions and other language constructs can accept the result of any expression, due to its unique nature, &lt;code&gt;isset()&lt;/code&gt; can only accept a variable, array key or object property as an argument.&lt;/p&gt;

&lt;p&gt;Attempting to use it any other way will result in a fatal error.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;if (isset('Hello world')) {
    // Fatal error: Cannot use isset() on the result of an expression (you can use "null !== expression" instead)
}

if (isset($a-&amp;gt;b())) {
    // Fatal error: Cannot use isset() on the result of an expression (you can use "null !== expression" instead)
}

if (isset(! $a)) {
    // Fatal error: Cannot use isset() on the result of an expression (you can use "null !== expression" instead)
}

if (isset(CONSTANT)) {
    // Fatal error: Cannot use isset() on the result of an expression (you can use "null !== expression" instead)
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h1&gt;
  
  
  It also checks if a value is &lt;code&gt;null&lt;/code&gt;
&lt;/h1&gt;

&lt;p&gt;&lt;code&gt;isset()&lt;/code&gt; will return &lt;code&gt;false&lt;/code&gt; if a variable is undefined OR if its value is &lt;code&gt;null&lt;/code&gt;. This may throw you off if &lt;code&gt;null&lt;/code&gt; is a proper value you have &lt;em&gt;set&lt;/em&gt; and want to allow.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$value = null;

if (isset($value)) {
    // ...
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;One way to go about checking this, depending on your requirements, is to check if a variable is defined in the current scope with &lt;code&gt;get_defined_vars()&lt;/code&gt; and examining the resulting array's keys.&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$value = null;

if (array_key_exists('value', get_defined_vars())) {
    // ...
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h1&gt;
  
  
  It can accept multiple arguments
&lt;/h1&gt;

&lt;p&gt;It's very common to see people chaining calls together to check that multiple values are set one-by-one.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;if (isset($a) &amp;amp;&amp;amp; isset($b) &amp;amp;&amp;amp; isset($c)) {
    // ...
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;However, &lt;code&gt;isset()&lt;/code&gt; is a variadic function that can accept any number of parameters at once to achieve the same effect, confirming if &lt;em&gt;all&lt;/em&gt; of the passed variables are set.&lt;/p&gt;

&lt;p&gt;This can be a great way to shorten long conditionals.&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;if (isset($a, $b, $c)) {
    // ...
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h1&gt;
  
  
  It does not trigger "undefined variable/index/property" notices
&lt;/h1&gt;

&lt;p&gt;If you're retrieving a value nested multiple levels deep, you probably want to make sure every step of the chain exists.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;if (isset($response, $response-&amp;gt;list, $response-&amp;gt;list['results'], $response-&amp;gt;list['results'][0])) {
    // ...
}

if (isset($arr[$key], $otherArr[$arr[$key]], $otherArr[$arr[$key]][$otherKey])) {
    // ...
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;However, &lt;code&gt;isset()&lt;/code&gt; will not trigger any "undefined variable", "undefined index" or "undefined property" notices, no matter how many layers you go through.&lt;/p&gt;

&lt;p&gt;This means that instead of confirming the value at every individual step, they can all be done in a single check:&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;if (isset($response-&amp;gt;list['results'][0])) {
    // ...
}

if (isset($otherArr[$arr[$key]][$otherKey])) {
    // ...
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h1&gt;
  
  
  "Undefined method" errors &lt;em&gt;do&lt;/em&gt; get triggered
&lt;/h1&gt;

&lt;p&gt;If a chain being checked happens to include a method call halfway through it, PHP will attempt to invoke the method.&lt;/p&gt;

&lt;p&gt;This means that if an earlier part of the chain does not exist, or the last value in the chain is an object that simply does not have this method, an error will still be triggered.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$a = new stdClass();

if (isset($a-&amp;gt;b()-&amp;gt;c)) {
    // Fatal error: Uncaught Error: Call to undefined method A::b()…
}

if (isset($a-&amp;gt;b-&amp;gt;c())) {
    // Fatal error: Uncaught Error: Call to a member function d() on null…
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;One way to deal with this is to be explicit in your conditional checks, stopping the chain and calling &lt;code&gt;method_exists()&lt;/code&gt; to verify the method exists every time it is needed.&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;if (isset($a) &amp;amp;&amp;amp; method_exists($a, 'b') &amp;amp;&amp;amp; isset($a-&amp;gt;b()-&amp;gt;c)) {
    // ...
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;One way to shorten such an expression is to use the &lt;a href="https://www.php.net/manual/en/language.operators.errorcontrol.php"&gt;error control operator&lt;/a&gt;, which suppresses any errors for a single expression. If an error is triggered, the operator will make the expression return &lt;code&gt;null&lt;/code&gt; instead and continue the execution.&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;if (@$a-&amp;gt;b()-&amp;gt;c !== null) {
    // ...
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;However, while this may be convenient, you should be aware that the error control operator is very inefficient and can also suppress errors triggered within the called methods you call and are not intending to suppress. It is not an outright replacement for &lt;code&gt;isset()&lt;/code&gt;.&lt;/p&gt;

&lt;h1&gt;
  
  
  &lt;strong&gt;!empty() is not quite the same&lt;/strong&gt;
&lt;/h1&gt;

&lt;p&gt;&lt;code&gt;[empty()](https://www.php.net/manual/en/function.empty.php)&lt;/code&gt; is also a language construct with similar behaviour to &lt;code&gt;isset()&lt;/code&gt; in that it doesn’t trigger undefined notices.&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$a = [];

if (empty($a['b']-&amp;gt;c)) {
   // ...
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;It seems as if it serves as a direct inverse of &lt;code&gt;isset()&lt;/code&gt;, but this is not the case. &lt;code&gt;empty()&lt;/code&gt; can also accept expressions as its arguments, but more importantly, &lt;strong&gt;it will type juggle&lt;/strong&gt; so that any falsey value is treated as such.&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$a = '0';

if (isset($a)) {
    // It IS set
}

if (empty($a)) {
    // It IS empty
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h1&gt;
  
  
  Null coalesce operator
&lt;/h1&gt;

&lt;p&gt;It is a very common occurrence to want to provide a fallback value in case a variable is not set. This is typically done with a short conditional &lt;code&gt;if&lt;/code&gt; statement or ternary clause.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$result = isset($value) ? $value : 'fallback';
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;As of PHP 7.0, this can be shortened using the null coalesce operator (&lt;code&gt;??&lt;/code&gt;) which will return the first value if it is set, or the second value if not.&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$result = $value ?? 'fallback';
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;If instead of returning a new value, you didn't want to set a new variable doing this, that is covered as well. As of PHP 7.4, the null coalesce assignment operator (&lt;code&gt;??=&lt;/code&gt;) allows an even shorter way to set a variable to a fallback if it isn't already set.&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$value ??= 'fallback';
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h1&gt;
  
  
  It does not evaluate the &lt;code&gt;__get()&lt;/code&gt; magic method
&lt;/h1&gt;

&lt;p&gt;Let's assume we have a pretty typical class that can dynamically get properties by using the magic method &lt;code&gt;__get()&lt;/code&gt; to retrieve a value.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class Person
{
    protected $attributes = [];

    public function __get($name)
    {
        return $this-&amp;gt;attributes[$name] ?? null;
    }

    public function __set($name, $value)
    {
        $this-&amp;gt;attributes[$name] = $value;
    }
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;If we use this class to set a property, we can make use of it as we might normally expect. However, if we check if the value is set, it will return &lt;code&gt;false&lt;/code&gt;&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$person = new Person();
$person-&amp;gt;name = 'Liam';

echo $person-&amp;gt;name; // 'Liam'

isset($person-&amp;gt;name); // false
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Wait, what's going on here?!&lt;/p&gt;

&lt;p&gt;Because &lt;code&gt;isset()&lt;/code&gt; is a language construct and not a regular function, the expression doesn't get evaluated before it's passed to it. Because &lt;code&gt;name&lt;/code&gt; isn't a real property on the object, it doesn't &lt;em&gt;really&lt;/em&gt; exist.&lt;/p&gt;

&lt;p&gt;However, when &lt;code&gt;isset()&lt;/code&gt; gets called on a property that doesn't exist or is inaccessible to the current scope (such as being protected or private), it will invoke a magic &lt;code&gt;__isset()&lt;/code&gt; method if the class has one defined. This allows for custom logic to be done to determine if we think the property we're checking is set according to our own rules.&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class Person
{
    // ...

    public function __isset($name)
    {
        return isset($this-&amp;gt;attributes[$name]);
    }
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;With that implemented, everything works as expected.&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$person = new Person();
$person-&amp;gt;name = 'Liam';

isset($person-&amp;gt;name); // true
isset($person-&amp;gt;somethingElse); // false
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;It is important to note that if you are checking nested properties, &lt;code&gt;__isset()&lt;/code&gt; will be invoked as appropriate for each property in the chain.&lt;/p&gt;

&lt;h1&gt;
  
  
  You &lt;em&gt;can&lt;/em&gt; pass non-existent variables to userland functions
&lt;/h1&gt;

&lt;p&gt;As we have already discussed, because &lt;code&gt;isset()&lt;/code&gt; is actually a language construct, it has special behaviour because of the PHP core, and thus does not behave like functions we define ourselves.&lt;/p&gt;

&lt;p&gt;However, we can achieve a similar effect in userland functions, through the use of &lt;em&gt;references&lt;/em&gt;. By doing this, we open up the possibility to expose additional functionality of our own choosing on top of the regular language construct.&lt;/p&gt;

&lt;p&gt;One practical example of this might be to treat any objects implementing a &lt;a href="https://en.wikipedia.org/wiki/Null_object_pattern"&gt;null object pattern&lt;/a&gt; as falsey values.&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;interface NullObject {}

class Logger {
    // ...
}

class NullLogger extends Logger implements NullObject {
    // ...
}

function is_truthy(&amp;amp;$value)
{
    if ($value instanceof NullObject) {
        return false;
    }

    return (bool) $value;
}

is_truthy($a);
// false

$b = '';
is_truthy($b);
// false

$c = '1';
is_truthy($c);
// true

$logger = new Logger();
is_truthy($logger);
// true

$nullLogger = new NullLogger();
is_truthy($nullLogger);
// false
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;However, references are not always that safe to use, as simply using them &lt;em&gt;can&lt;/em&gt; affect the original value, even if the function doesn't explicitly do it.&lt;/p&gt;

&lt;p&gt;For example, any undefined array keys or properties will automatically be assigned and their value set to &lt;code&gt;null&lt;/code&gt;&lt;/p&gt;


&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$a = [];

&lt;p&gt;is_truthy($a['b']['c']);&lt;br&gt;
// false&lt;/p&gt;

&lt;p&gt;var_dump($a);&lt;br&gt;
// [&lt;br&gt;
//     'b' =&amp;gt; [&lt;br&gt;
//         'c' =&amp;gt; null,&lt;br&gt;
//     ],&lt;br&gt;
// ]&lt;br&gt;
&lt;/p&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h1&gt;
&lt;br&gt;
  &lt;br&gt;
  &lt;br&gt;
  Conclusion&lt;br&gt;
&lt;/h1&gt;

&lt;p&gt;Hopefully throughout this look at &lt;code&gt;isset()&lt;/code&gt;, its behaviour and other related things, you will have picked something up that will help you make your code cleaner, more explicit, and not catch you out in edge cases when you need to check if a variable has been set.&lt;/p&gt;

</description>
      <category>php</category>
      <category>isset</category>
    </item>
    <item>
      <title>Why I Prefer Discord Over Slack</title>
      <dc:creator>Liam Hammett</dc:creator>
      <pubDate>Fri, 14 Sep 2018 18:39:42 +0000</pubDate>
      <link>https://dev.to/liamhammett/why-i-prefer-discord-overslack-1i4k</link>
      <guid>https://dev.to/liamhammett/why-i-prefer-discord-overslack-1i4k</guid>
      <description>&lt;p&gt;I've used more than my fair share of chat apps and solutions in the past, for both personal and professional use. AIM, MSN, Skype, IRC, Pidgin, HipChat, and Mattermost  -  to name the few that I've used for at least a year.&lt;/p&gt;

&lt;p&gt;But over the years, they've all mostly faded away with two standing at the forefront: &lt;strong&gt;Slack for professional use&lt;/strong&gt;, and &lt;strong&gt;Discord for gaming and casual use&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;I use both on a daily basis, but strongly prefer Discord out of these two options, for both general use and coding discussion. Here I'm going to explain why Discord is better.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--J7PaAwHm--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/1000/0%2AIjkUzF5pJhFbNySx.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--J7PaAwHm--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/1000/0%2AIjkUzF5pJhFbNySx.png" alt="Discord  -  free voice and text chat for gamers"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Multiple servers work seamlessly
&lt;/h2&gt;

&lt;p&gt;There are a ton of chat communities - and I'm certainly in more than one, and may dip in-and-out of even more every now and again.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Slack&lt;/strong&gt; makes this a pain because of the way it tries to separate every server to be standalone. This certainly has its use in certain environments but is not ideal when you're in multiple servers. On the web app only a single server can be opened at once and every time you join a new server you essentially have to "sign up" for it again.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Discord&lt;/strong&gt; makes it a breeze by having a single account for everything. You still get to have per-server nicknames and identities, but you can join a new server with the mere click of a link, all of your notifications get neatly handled in the same way, and you can quickly make new servers on-the-fly. It also gives you the ability to see which users are in mutual servers, create DMs and groups outside the server itself, and a bunch of other convenient features.&lt;/p&gt;

&lt;h2&gt;
  
  
  Working with code
&lt;/h2&gt;

&lt;p&gt;I'm a programmer, so in any given day there's a good chance I'll write or paste a snippet of code to discuss it with someone.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Slack&lt;/strong&gt; has options for doing this - using standard markdown you can write inline or multiline code snippets. Alternatively, you can insert a snippet from a modal window, selecting the programming language the code is in and having that syntax highlight it as it puts it in chat.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Discord&lt;/strong&gt; feels a lot more natural. There's no popup window to insert code - you do it all from the chat bar. You can write GitHub Flavored Markdown to tell what language it's in and therefore the syntax highlighting. There are little things that stop you from messing up too, like when you're writing a multi-line code block, hitting the 'return' key will insert a new line instead of submitting the message.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--pFZFYT-b--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://cdn-images-1.medium.com/max/1000/1%2A3rN8CnVRB2C4RpLFw_91gA.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--pFZFYT-b--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://cdn-images-1.medium.com/max/1000/1%2A3rN8CnVRB2C4RpLFw_91gA.gif" alt="Writing a multi-line code block in Discord's chat"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;(Writing a multi-line code block in Discord's chat)&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  It's free
&lt;/h2&gt;

&lt;p&gt;I'm not one to pass up paying for a decent product, especially one I'll be using on a daily basis, but it certainly comes into question here.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Slack&lt;/strong&gt; has a free plan, but it only lets you store the last 10,000 messages. Remember a discussion from a few weeks back that you need to reference or get the context of a conversation? Maybe someone posted a code snippet to help someone else and now you've run into the same problem? Tough luck getting that back. Of course, Slack will let you keep unlimited messages, *for £6.30+ per user in the server - which is not practical for community servers.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Discord&lt;/strong&gt; on the other hand, is completely free. Unlimited messages (I still have messages and files from &lt;em&gt;three years ago&lt;/em&gt; accessible in Discord), group calls, custom emojis and everything else you could want from a chat application. They offer a paid subscription service called Nitro, but that only gives some additional cosmetic benefits, it's not required by any means for the core application.&lt;/p&gt;

&lt;h2&gt;
  
  
  Uptime
&lt;/h2&gt;

&lt;p&gt;It's a bit disappointing that this is even a concern but certainly is an important concern.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Slack&lt;/strong&gt; has made me experience several hours of downtime multiple times over the last few months, which is a bit of a problem when it's a primary communication method with co-workers. Slack does offer a 99.9% uptime SLA but at &lt;em&gt;double the cost&lt;/em&gt; of the paid plan.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Discord&lt;/strong&gt; has also had its fair share of uptime problems over the years, especially back in 2017, but even when Discord is having problems it rarely affects the whole platform. Even if a server is down, odds are you still have access to DM the people on that server if you need to get in touch.&lt;/p&gt;

&lt;h2&gt;
  
  
  Privacy in DMs
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Slack&lt;/strong&gt;, simply put, allows the server owners to export the entire chat history - including direct messages you may have made in private. Want to talk about something personal in private you might not want your boss to know about? Don't do it on Slack.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Discord&lt;/strong&gt; doesn't offer any of this - which may, of course, be less than ideal if you're in an industry that's regulating you to archive all messages, but then you probably wouldn't be using Discord in the first place.&lt;/p&gt;

&lt;h2&gt;
  
  
  Personality
&lt;/h2&gt;

&lt;p&gt;How can an application have a personality? There are lots of ways - how things are worded, what graphics there are, the little things that make you smile and just enjoy the experience of using an application.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Slack&lt;/strong&gt; has a personality. It's playful and friendly in some places, like in the wording when you add too many reactions to a message. It's very limited though, the application itself still feels corporate and impersonal - which is perfect for its target audience, but not for everyone.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--fAFjLPjX--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/1000/1%2AiTNLjGIFQsConBd4zKK8ZQ.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--fAFjLPjX--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/1000/1%2AiTNLjGIFQsConBd4zKK8ZQ.png" alt="Slack popup upon adding too many reactions"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;(Slack popup upon adding too many reactions)&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Discord&lt;/strong&gt;, on the other hand, has a ton of personality. Its target market is gamers, and it plays on that well. The application is full of personality, from the random community-submitted loading messages, playful custom emoji, and colourful members lists. There are memes and jokes all around, a solid and fun art style, the changelogs are an absolute pleasure to read, and there are so many little things that are in the application that you wouldn't expect, but are nice when you come across one - like the emoji picker changes to a random emoji whenever you mouse over it.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--PPu0OWSC--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://cdn-images-1.medium.com/max/1000/1%2AuWgv_mSfPuDJmg-ZQVtM4Q.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--PPu0OWSC--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://cdn-images-1.medium.com/max/1000/1%2AuWgv_mSfPuDJmg-ZQVtM4Q.gif" alt="Discord emoji picker"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;(Discord emoji picker)&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--RuY7xpTi--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/1000/1%2ANWoaOhMktYtVqAlq-ker7g.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--RuY7xpTi--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/1000/1%2ANWoaOhMktYtVqAlq-ker7g.png" alt="Discord changelog excerpt from August 8 2018"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;(Discord changelog excerpt from August 8 2018)&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  It's not perfect, though
&lt;/h2&gt;

&lt;p&gt;While Slack is a pleasure to use all around, it's not got everything. The gaming vibe might not be for everyone, there's no way to white-label it or get an SLA, export everything easily, but that's not all as much as a concern for me.&lt;/p&gt;

&lt;p&gt;However, there are some things at the top of my "want" list for Discord:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Discord has very few &lt;strong&gt;professional-level integrations&lt;/strong&gt; like Slack does. There's a very vibrant bot community, but still, a lot of work to be done to get things like JIRA, Trello, GitHub and the like on the same level of integration.&lt;/li&gt;
&lt;li&gt;Slack has neat interactive message features for bots, like &lt;strong&gt;question buttons and menus&lt;/strong&gt;, which make interacting with these bots much more convenient.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Threaded messages&lt;/strong&gt;. I don't use them often in Slack, but they really do help keep conversations in context without disrupting the main flow of conversation.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Keyboard accessibility&lt;/strong&gt;. I admit I've not looked into this much, but you seemingly can't seem to navigate around the Discord interface easily with keyboard controls.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;What're your thoughts on these chat applications? Did I miss anything that makes one any better or worse than its competitor?&lt;/p&gt;

</description>
      <category>slack</category>
      <category>discord</category>
      <category>messaging</category>
      <category>communication</category>
    </item>
  </channel>
</rss>
