diff --git a/src/main/java/net/knarcraft/stargate/Stargate.java b/src/main/java/net/knarcraft/stargate/Stargate.java
index 0c3b72c..2eecae4 100644
--- a/src/main/java/net/knarcraft/stargate/Stargate.java
+++ b/src/main/java/net/knarcraft/stargate/Stargate.java
@@ -405,7 +405,7 @@ public class Stargate extends JavaPlugin {
 
         //If users have an outdated config, assume they also need to update their default gates
         if (isMigrating) {
-            GateHandler.populateDefaults(gateFolder);
+            GateHandler.writeDefaultGatesToFolder(gateFolder);
         }
 
         //Gates
diff --git a/src/main/java/net/knarcraft/stargate/portal/GateHandler.java b/src/main/java/net/knarcraft/stargate/portal/GateHandler.java
index fa20d6a..b6f0087 100644
--- a/src/main/java/net/knarcraft/stargate/portal/GateHandler.java
+++ b/src/main/java/net/knarcraft/stargate/portal/GateHandler.java
@@ -2,6 +2,7 @@ package net.knarcraft.stargate.portal;
 
 import net.knarcraft.stargate.Stargate;
 import net.knarcraft.stargate.utility.EconomyHandler;
+import net.knarcraft.stargate.utility.GateReader;
 import net.knarcraft.stargate.utility.MaterialHelper;
 import org.bukkit.Material;
 import org.bukkit.block.Block;
@@ -17,6 +18,10 @@ import java.util.Scanner;
 import java.util.Set;
 import java.util.logging.Level;
 
+import static net.knarcraft.stargate.utility.GateReader.generateLayoutMatrix;
+import static net.knarcraft.stargate.utility.GateReader.readGateConfig;
+import static net.knarcraft.stargate.utility.GateReader.readGateFile;
+
 /**
  * The gate handler keeps track of all gates
  */
@@ -93,10 +98,10 @@ public class GateHandler {
     }
 
     /**
-     * Loads a gate
+     * Loads a gate from a file
      *
-     * @param file <p>The file containing the gate's layout</p>
-     * @return <p>The loaded gate or null if unable to load the gate</p>
+     * @param file <p>The file containing the gate data</p>
+     * @return <p>The loaded gate, or null if unable to load the gate</p>
      */
     private static Gate loadGate(File file) {
         try (Scanner scanner = new Scanner(file)) {
@@ -108,11 +113,11 @@ public class GateHandler {
     }
 
     /**
-     * Loads a gate
+     * Loads a gate from a file
      *
-     * @param fileName     <p>The name of the file containing the gate layout</p>
-     * @param parentFolder <p>The parent folder of the layout file</p>
-     * @param scanner      <p>The scanner to use for reading the gate layout</p>
+     * @param fileName     <p>The name of the file containing the gate data</p>
+     * @param parentFolder <p>The parent folder of the gate data file</p>
+     * @param scanner      <p>The scanner to use for reading the gate data</p>
      * @return <p>The loaded gate or null if unable to load the gate</p>
      */
     private static Gate loadGate(String fileName, String parentFolder, Scanner scanner) {
@@ -121,17 +126,17 @@ public class GateHandler {
         Map<String, String> config = new HashMap<>();
         Set<Material> frameTypes = new HashSet<>();
 
-        //Initialize types map
+        //Initialize character to material map
         characterMaterialMap.put(ENTRANCE, Material.AIR);
         characterMaterialMap.put(EXIT, Material.AIR);
         characterMaterialMap.put(ANYTHING, Material.AIR);
 
         //Read the file into appropriate lists and maps
-        int cols = readGateFile(scanner, characterMaterialMap, fileName, design, frameTypes, config);
-        if (cols < 0) {
+        int columns = readGateFile(scanner, characterMaterialMap, fileName, design, frameTypes, config);
+        if (columns < 0) {
             return null;
         }
-        Character[][] layout = generateLayoutMatrix(design, cols);
+        Character[][] layout = generateLayoutMatrix(design, columns);
 
         //Create and validate the new gate
         Gate gate = createGate(config, fileName, layout, characterMaterialMap);
@@ -139,7 +144,8 @@ public class GateHandler {
             return null;
         }
 
-        gate.save(parentFolder + "/"); // Updates format for version changes
+        //Update gate file in case the format has changed between versions
+        gate.save(parentFolder + "/");
         return gate;
     }
 
@@ -149,23 +155,26 @@ public class GateHandler {
      * @param config               <p>The config map to get configuration values from</p>
      * @param fileName             <p>The name of the saved gate config file</p>
      * @param layout               <p>The layout matrix of the new gate</p>
-     * @param characterMaterialMap <p>The mapping for used gate material types</p>
-     * @return <p>A new gate or null if the config is invalid</p>
+     * @param characterMaterialMap <p>A map between layout characters and the material to use</p>
+     * @return <p>A new gate, or null if the config is invalid</p>
      */
     private static Gate createGate(Map<String, String> config, String fileName, Character[][] layout,
                                    Map<Character, Material> characterMaterialMap) {
-        Material portalOpenBlock = readConfig(config, fileName, "portal-open", defaultPortalBlockOpen);
-        Material portalClosedBlock = readConfig(config, fileName, "portal-closed", defaultPortalBlockClosed);
-        Material portalButton = readConfig(config, fileName, "button", defaultButton);
-        int useCost = readConfig(config, fileName, "usecost");
-        int createCost = readConfig(config, fileName, "createcost");
-        int destroyCost = readConfig(config, fileName, "destroycost");
+        //Read relevant material types
+        Material portalOpenBlock = readGateConfig(config, fileName, "portal-open", defaultPortalBlockOpen);
+        Material portalClosedBlock = readGateConfig(config, fileName, "portal-closed", defaultPortalBlockClosed);
+        Material portalButton = readGateConfig(config, fileName, "button", defaultButton);
+
+        //Read economy values
+        int useCost = GateReader.readGateConfig(config, fileName, "usecost");
+        int createCost = GateReader.readGateConfig(config, fileName, "createcost");
+        int destroyCost = GateReader.readGateConfig(config, fileName, "destroycost");
         boolean toOwner = (config.containsKey("toowner") ? Boolean.parseBoolean(config.get("toowner")) :
                 EconomyHandler.toOwner);
 
+        //Create the new gate
         Gate gate = new Gate(fileName, new GateLayout(layout), characterMaterialMap, portalOpenBlock, portalClosedBlock,
-                portalButton, useCost,
-                createCost, destroyCost, toOwner);
+                portalButton, useCost, createCost, destroyCost, toOwner);
 
         if (!validateGate(gate, fileName)) {
             return null;
@@ -174,7 +183,7 @@ public class GateHandler {
     }
 
     /**
-     * Validate that a gate is valid
+     * Validates that a gate is valid
      *
      * @param gate     <p>The gate to validate</p>
      * @param fileName <p>The filename of the loaded gate file</p>
@@ -195,177 +204,6 @@ public class GateHandler {
         return true;
     }
 
-    /**
-     * Generates a matrix storing the gate layout
-     *
-     * @param design <p>The design of the gate layout</p>
-     * @param cols   <p>The largest amount of columns in the design</p>
-     * @return <p>A matrix containing the gate's layout</p>
-     */
-    private static Character[][] generateLayoutMatrix(List<List<Character>> design, int cols) {
-        Character[][] layout = new Character[design.size()][cols];
-        for (int lineIndex = 0; lineIndex < design.size(); lineIndex++) {
-            List<Character> row = design.get(lineIndex);
-            Character[] result = new Character[cols];
-
-            for (int rowIndex = 0; rowIndex < cols; rowIndex++) {
-                if (rowIndex < row.size()) {
-                    result[rowIndex] = row.get(rowIndex);
-                } else {
-                    //Add spaces to all lines which are too short
-                    result[rowIndex] = ' ';
-                }
-            }
-
-            layout[lineIndex] = result;
-        }
-        return layout;
-    }
-
-    /**
-     * Reads the gate file
-     *
-     * @param scanner              <p>The scanner to read from</p>
-     * @param characterMaterialMap <p>The map of characters to store valid symbols in</p>
-     * @param fileName             <p>The filename of the loaded gate config file</p>
-     * @param design               <p>The list to store the loaded design to</p>
-     * @param frameTypes           <p>The set of gate frame types to store to</p>
-     * @param config               <p>The map of config values to store to</p>
-     * @return <p>The column count/width of the loaded gate</p>
-     */
-    private static int readGateFile(Scanner scanner, Map<Character, Material> characterMaterialMap, String fileName,
-                                    List<List<Character>> design, Set<Material> frameTypes, Map<String, String> config) {
-        boolean designing = false;
-        int cols = 0;
-        try {
-            while (scanner.hasNextLine()) {
-                String line = scanner.nextLine();
-
-                if (designing) {
-                    cols = readGateDesignLine(line, cols, characterMaterialMap, fileName, design);
-                    if (cols < 0) {
-                        return -1;
-                    }
-                } else {
-                    if (!line.isEmpty() && !line.startsWith("#")) {
-                        readGateConfigValue(line, characterMaterialMap, frameTypes, config);
-                    } else if ((line.isEmpty()) || (!line.contains("=") && !line.startsWith("#"))) {
-                        designing = true;
-                    }
-                }
-            }
-        } catch (Exception ex) {
-            Stargate.logger.log(Level.SEVERE, "Could not load Gate " + fileName + " - " + ex.getMessage());
-            return -1;
-        } finally {
-            if (scanner != null) {
-                scanner.close();
-            }
-        }
-        return cols;
-    }
-
-    /**
-     * Reads one design line of the gate layout file
-     *
-     * @param line     <p>The line to read</p>
-     * @param cols     <p>The current max columns value of the design</p>
-     * @param characterMaterialMap    <p>The map of characters to check for valid symbols</p>
-     * @param fileName <p>The filename of the loaded gate config file</p>
-     * @param design   <p>The list to store the loaded design to</p>
-     * @return <p>The new max columns value of the design</p>
-     */
-    private static int readGateDesignLine(String line, int cols, Map<Character, Material> characterMaterialMap,
-                                          String fileName, List<List<Character>> design) {
-        List<Character> row = new ArrayList<>();
-
-        if (line.length() > cols) {
-            cols = line.length();
-        }
-
-        for (Character symbol : line.toCharArray()) {
-            if ((symbol.equals('?')) || (!characterMaterialMap.containsKey(symbol))) {
-                Stargate.logger.log(Level.SEVERE, "Could not load Gate " + fileName + " - Unknown symbol '" +
-                        symbol + "' in diagram");
-                return -1;
-            }
-            row.add(symbol);
-        }
-
-        design.add(row);
-        return cols;
-    }
-
-    /**
-     * Reads one config value from the gate layout file
-     *
-     * @param line       <p>The line to read</p>
-     * @param characterMaterialMap      <p>The map of characters to materials to store to</p>
-     * @param frameTypes <p>The set of gate frame types to store to</p>
-     * @param config     <p>The map of config values to store to</p>
-     * @throws Exception <p>If an invalid material is encountered</p>
-     */
-    private static void readGateConfigValue(String line, Map<Character, Material> characterMaterialMap,
-                                            Set<Material> frameTypes, Map<String, String> config) throws Exception {
-        String[] split = line.split("=");
-        String key = split[0].trim();
-        String value = split[1].trim();
-
-        if (key.length() == 1) {
-            Character symbol = key.charAt(0);
-            Material id = Material.getMaterial(value);
-            if (id == null) {
-                throw new Exception("Invalid material in line: " + line);
-            }
-            characterMaterialMap.put(symbol, id);
-            frameTypes.add(id);
-        } else {
-            config.put(key, value);
-        }
-    }
-
-    /**
-     * Reads an integer configuration key
-     *
-     * @param config   <p>The configuration to read</p>
-     * @param fileName <p>The filename of the config file</p>
-     * @param key      <p>The config key to read</p>
-     * @return <p>The read value, or -1 if it cannot be read</p>
-     */
-    private static int readConfig(Map<String, String> config, String fileName, String key) {
-        if (config.containsKey(key)) {
-            try {
-                return Integer.parseInt(config.get(key));
-            } catch (NumberFormatException ex) {
-                Stargate.logger.log(Level.WARNING, String.format("%s reading %s: %s is not numeric",
-                        ex.getClass().getName(), fileName, key));
-            }
-        }
-
-        return -1;
-    }
-
-    /**
-     * Gets the material defined in the config
-     *
-     * @param config          <p>The config to read</p>
-     * @param fileName        <p>The config file the config belongs to</p>
-     * @param key             <p>The config key to read</p>
-     * @param defaultMaterial <p>The default material to use, in case the config is invalid</p>
-     * @return <p>The material to use</p>
-     */
-    private static Material readConfig(Map<String, String> config, String fileName, String key, Material defaultMaterial) {
-        if (config.containsKey(key)) {
-            Material material = Material.getMaterial(config.get(key));
-            if (material != null) {
-                return material;
-            } else {
-                Stargate.logger.log(Level.WARNING, String.format("Error reading %s: %s is not a material", fileName, key));
-            }
-        }
-        return defaultMaterial;
-    }
-
     /**
      * Loads all gates inside the given folder
      *
@@ -376,17 +214,20 @@ public class GateHandler {
         File[] files;
 
         if (directory.exists()) {
+            //Get all files with a .gate extension
             files = directory.listFiles((file) -> file.isFile() && file.getName().endsWith(".gate"));
         } else {
+            //Set files to empty list to signal that default gates need to be copied
             files = new File[0];
         }
 
         if (files == null || files.length == 0) {
             //The gates-folder was not found. Assume this is the first run
             if (directory.mkdir()) {
-                populateDefaults(gateFolder);
+                writeDefaultGatesToFolder(gateFolder);
             }
         } else {
+            //Load and register the corresponding gate for each file
             for (File file : files) {
                 Gate gate = loadGate(file);
                 if (gate != null) {
@@ -397,11 +238,11 @@ public class GateHandler {
     }
 
     /**
-     * Writes the default gate specifications to the given folder
+     * Writes the default gates to the given folder
      *
      * @param gateFolder <p>The folder containing gate config files</p>
      */
-    public static void populateDefaults(String gateFolder) {
+    public static void writeDefaultGatesToFolder(String gateFolder) {
         loadGateFromJar("nethergate.gate", gateFolder);
         loadGateFromJar("watergate.gate", gateFolder);
         loadGateFromJar("endgate.gate", gateFolder);
@@ -414,9 +255,11 @@ public class GateHandler {
      * @param gateFolder <p>The folder containing gates</p>
      */
     private static void loadGateFromJar(String gateFile, String gateFolder) {
+        //Get an input stream for the internal file
         InputStream gateFileStream = Gate.class.getResourceAsStream("/gates/" + gateFile);
         if (gateFileStream != null) {
             Scanner scanner = new Scanner(gateFileStream);
+            //Load and register the gate
             Gate gate = loadGate(gateFile, gateFolder, scanner);
             if (gate != null) {
                 registerGate(gate);
@@ -440,6 +283,9 @@ public class GateHandler {
     /**
      * Gets the gates with the given control block
      *
+     * <p>The control block is the block type where the sign should be placed. It is used to decide whether a user
+     * is creating a new portal.</p>
+     *
      * @param type <p>The type of the control block to check</p>
      * @return <p>A list of gates using the given material for control block</p>
      */
@@ -455,13 +301,13 @@ public class GateHandler {
     }
 
     /**
-     * Gets a portal by its name (filename before .gate)
+     * Gets a portal given its filename
      *
-     * @param name <p>The name of the gate to get</p>
-     * @return <p>The gate with the given name</p>
+     * @param fileName <p>The filename of the gate to get</p>
+     * @return <p>The gate with the given filename</p>
      */
-    public static Gate getGateByName(String name) {
-        return gates.get(name);
+    public static Gate getGateByName(String fileName) {
+        return gates.get(fileName);
     }
 
     /**
@@ -474,7 +320,7 @@ public class GateHandler {
     }
 
     /**
-     * Clears all loaded gates
+     * Clears all loaded gates and control blocks
      */
     public static void clearGates() {
         gates.clear();
diff --git a/src/main/java/net/knarcraft/stargate/utility/GateReader.java b/src/main/java/net/knarcraft/stargate/utility/GateReader.java
new file mode 100644
index 0000000..a04a10a
--- /dev/null
+++ b/src/main/java/net/knarcraft/stargate/utility/GateReader.java
@@ -0,0 +1,212 @@
+package net.knarcraft.stargate.utility;
+
+import net.knarcraft.stargate.Stargate;
+import org.bukkit.Material;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.Scanner;
+import java.util.Set;
+import java.util.logging.Level;
+
+/**
+ * Helper class for reading gate files
+ */
+public final class GateReader {
+
+    private GateReader() {
+
+    }
+
+    /**
+     * Reads a gate file
+     *
+     * @param scanner              <p>The scanner to read from</p>
+     * @param characterMaterialMap <p>The map of characters to store valid symbols in</p>
+     * @param fileName             <p>The filename of the loaded gate config file</p>
+     * @param design               <p>The list to store the loaded design/layout to</p>
+     * @param frameTypes           <p>The set to store frame/border materials to</p>
+     * @param config               <p>The map of config values to store to</p>
+     * @return <p>The column count/width of the loaded gate</p>
+     */
+    public static int readGateFile(Scanner scanner, Map<Character, Material> characterMaterialMap, String fileName,
+                                   List<List<Character>> design, Set<Material> frameTypes, Map<String, String> config) {
+        boolean designing = false;
+        int columns = 0;
+        try {
+            while (scanner.hasNextLine()) {
+                String line = scanner.nextLine();
+
+                if (designing) {
+                    //If we have reached the gate's layout/design, read it
+                    columns = readGateDesignLine(line, columns, characterMaterialMap, fileName, design);
+                    if (columns < 0) {
+                        return -1;
+                    }
+                } else {
+                    if (!line.isEmpty() && !line.startsWith("#")) {
+                        //Read a normal config value
+                        readGateConfigValue(line, characterMaterialMap, frameTypes, config);
+                    } else if ((line.isEmpty()) || (!line.contains("=") && !line.startsWith("#"))) {
+                        //An empty line marks the start of the gate's layout/design
+                        designing = true;
+                    }
+                }
+            }
+        } catch (Exception ex) {
+            Stargate.logger.log(Level.SEVERE, "Could not load Gate " + fileName + " - " + ex.getMessage());
+            return -1;
+        } finally {
+            if (scanner != null) {
+                scanner.close();
+            }
+        }
+        return columns;
+    }
+
+    /**
+     * Reads one design line of the gate layout file
+     *
+     * <p>The max columns value is sent through this method in such a way that when the last gate design line is read,
+     * the max columns value contains the largest amount of columns (character) found in any of the design's lines.</p>
+     *
+     * @param line                 <p>The line to read</p>
+     * @param maxColumns           <p>The current max columns value of the design</p>
+     * @param characterMaterialMap <p>The map between characters and the corresponding materials to use</p>
+     * @param fileName             <p>The filename of the loaded gate config file</p>
+     * @param design               <p>The two-dimensional list to store the loaded design to</p>
+     * @return <p>The new max columns value of the design</p>
+     */
+    private static int readGateDesignLine(String line, int maxColumns, Map<Character, Material> characterMaterialMap,
+                                          String fileName, List<List<Character>> design) {
+        List<Character> row = new ArrayList<>();
+
+        //Update the max columns number if this line has more columns
+        if (line.length() > maxColumns) {
+            maxColumns = line.length();
+        }
+
+        for (Character symbol : line.toCharArray()) {
+            //Refuse read gate designs with unknown characters
+            if (symbol.equals('?') || (!characterMaterialMap.containsKey(symbol))) {
+                Stargate.logger.log(Level.SEVERE, "Could not load Gate " + fileName + " - Unknown symbol '" +
+                        symbol + "' in diagram");
+                return -1;
+            }
+            //Add the read character to the row
+            row.add(symbol);
+        }
+
+        //Add this row of the gate's design to the two-dimensional design list
+        design.add(row);
+        return maxColumns;
+    }
+
+    /**
+     * Reads one config value from the gate layout file
+     *
+     * @param line                 <p>The line to read</p>
+     * @param characterMaterialMap <p>The character to material map to store to</p>
+     * @param frameTypes           <p>The set to store gate frame/border types to</p>
+     * @param config               <p>The config value map to store to</p>
+     * @throws Exception <p>If an invalid material is encountered</p>
+     */
+    private static void readGateConfigValue(String line, Map<Character, Material> characterMaterialMap,
+                                            Set<Material> frameTypes, Map<String, String> config) throws Exception {
+        String[] split = line.split("=");
+        String key = split[0].trim();
+        String value = split[1].trim();
+
+        if (key.length() == 1) {
+            //Read a gate frame material
+            Character symbol = key.charAt(0);
+            Material material = Material.getMaterial(value);
+            if (material == null) {
+                throw new Exception("Invalid material in line: " + line);
+            }
+            //Register the map between the read symbol and the corresponding material
+            characterMaterialMap.put(symbol, material);
+            //Save the material as one of the frame materials used for this kind of gate
+            frameTypes.add(material);
+        } else {
+            //Read a normal config value
+            config.put(key, value);
+        }
+    }
+
+    /**
+     * Reads an integer configuration value
+     *
+     * @param config   <p>The configuration to read</p>
+     * @param fileName <p>The filename of the config file</p>
+     * @param key      <p>The config key to read</p>
+     * @return <p>The read value, or -1 if it could not be read</p>
+     */
+    public static int readGateConfig(Map<String, String> config, String fileName, String key) {
+        if (config.containsKey(key)) {
+            try {
+                return Integer.parseInt(config.get(key));
+            } catch (NumberFormatException ex) {
+                Stargate.logger.log(Level.WARNING, String.format("%s reading %s: %s is not numeric",
+                        ex.getClass().getName(), fileName, key));
+            }
+        }
+
+        return -1;
+    }
+
+    /**
+     * Reads a material configuration value
+     *
+     * @param config          <p>The configuration to read</p>
+     * @param fileName        <p>The filename of the config file</p>
+     * @param key             <p>The config key to read</p>
+     * @param defaultMaterial <p>The default material to use, in case the config is invalid</p>
+     * @return <p>The material specified in the config, or the default material if it could not be read</p>
+     */
+    public static Material readGateConfig(Map<String, String> config, String fileName, String key,
+                                          Material defaultMaterial) {
+        if (config.containsKey(key)) {
+            Material material = Material.getMaterial(config.get(key));
+            if (material != null) {
+                return material;
+            } else {
+                Stargate.logger.log(Level.WARNING, String.format("Error reading %s: %s is not a material", fileName,
+                        key));
+            }
+        }
+        return defaultMaterial;
+    }
+
+    /**
+     * Generates a matrix containing the gate layout
+     *
+     * <p>This basically changes the list of lists into a primitive matrix. Additionally, spaces are added to the end of
+     * each row which to too short relative to the longest row.</p>
+     *
+     * @param design  <p>The design of the gate layout</p>
+     * @param columns <p>The largest amount of columns in the design</p>
+     * @return <p>A matrix containing the gate's layout</p>
+     */
+    public static Character[][] generateLayoutMatrix(List<List<Character>> design, int columns) {
+        Character[][] layout = new Character[design.size()][columns];
+        for (int lineIndex = 0; lineIndex < design.size(); lineIndex++) {
+            List<Character> row = design.get(lineIndex);
+            Character[] result = new Character[columns];
+
+            for (int rowIndex = 0; rowIndex < columns; rowIndex++) {
+                if (rowIndex < row.size()) {
+                    result[rowIndex] = row.get(rowIndex);
+                } else {
+                    //Add spaces to all lines which are too short
+                    result[rowIndex] = ' ';
+                }
+            }
+
+            layout[lineIndex] = result;
+        }
+        return layout;
+    }
+
+}