diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
index 02dd0e61..973b3ff2 100644
--- a/.github/workflows/test.yml
+++ b/.github/workflows/test.yml
@@ -21,7 +21,7 @@ jobs:
   test:
     strategy:
       matrix:
-        go-version: [1.18.x, 1.19.x, 1.20.x, 1.21.x]
+        go-version: [1.20.x, 1.21.x, 1.22.x]
         os: [ubuntu-latest]
     runs-on: ${{ matrix.os }}
     timeout-minutes: 10
diff --git a/.github/workflows/validate.yml b/.github/workflows/validate.yml
index 3eff0426..8758876e 100644
--- a/.github/workflows/validate.yml
+++ b/.github/workflows/validate.yml
@@ -21,7 +21,7 @@ jobs:
   linters:
     strategy:
       matrix:
-        go-version: [1.21.x]
+        go-version: [1.22.x]
         os: [ubuntu-latest]
     runs-on: ${{ matrix.os }}
     timeout-minutes: 10
@@ -35,5 +35,5 @@ jobs:
       - name: lint
         uses: golangci/golangci-lint-action@v3
         with:
-          version: v1.55.1
+          version: v1.57.2
           args: --print-resources-usage --timeout=10m --verbose
diff --git a/Makefile b/Makefile
index dfcee7d2..00e528a6 100644
--- a/Makefile
+++ b/Makefile
@@ -1,5 +1,5 @@
 
-# NOTE: This Makefile is only necessary if you 
+# NOTE: This Makefile is only necessary if you
 # plan on developing the msgp tool and library.
 # Installation can still be performed with a
 # normal `go install`.
@@ -55,8 +55,8 @@ ci: prepare
 	if [ `arch` == 'x86_64' ]; then \
 		sudo apt-get -y -q update; \
 		sudo apt-get -y -q install build-essential; \
-		wget -q https://github.com/tinygo-org/tinygo/releases/download/v0.30.0/tinygo_0.30.0_amd64.deb; \
-		sudo dpkg -i tinygo_0.30.0_amd64.deb; \
+		wget -q https://github.com/tinygo-org/tinygo/releases/download/v0.32.0/tinygo_0.32.0_amd64.deb; \
+		sudo dpkg -i tinygo_0.32.0_amd64.deb; \
 		export PATH=$$PATH:/usr/local/tinygo/bin; \
 	fi
 	go test -v ./... ./_generated
diff --git a/_generated/omitempty_test.go b/_generated/omitempty_test.go
index 75469c1b..6df9149d 100644
--- a/_generated/omitempty_test.go
+++ b/_generated/omitempty_test.go
@@ -2,7 +2,7 @@ package _generated
 
 import (
 	"bytes"
-	"io/ioutil"
+	"io"
 	"testing"
 
 	"github.com/tinylib/msgp/msgp"
@@ -146,7 +146,7 @@ func TestOmitEmptyLotsOFields(t *testing.T) {
 }
 
 func BenchmarkOmitEmpty10AllEmpty(b *testing.B) {
-	en := msgp.NewWriter(ioutil.Discard)
+	en := msgp.NewWriter(io.Discard)
 	var s OmitEmpty10
 
 	b.ResetTimer()
@@ -160,7 +160,7 @@ func BenchmarkOmitEmpty10AllEmpty(b *testing.B) {
 }
 
 func BenchmarkOmitEmpty10AllFull(b *testing.B) {
-	en := msgp.NewWriter(ioutil.Discard)
+	en := msgp.NewWriter(io.Discard)
 	var s OmitEmpty10
 	s.Field00 = "this is the value of field00"
 	s.Field01 = "this is the value of field01"
@@ -184,7 +184,7 @@ func BenchmarkOmitEmpty10AllFull(b *testing.B) {
 }
 
 func BenchmarkNotOmitEmpty10AllEmpty(b *testing.B) {
-	en := msgp.NewWriter(ioutil.Discard)
+	en := msgp.NewWriter(io.Discard)
 	var s NotOmitEmpty10
 
 	b.ResetTimer()
@@ -198,7 +198,7 @@ func BenchmarkNotOmitEmpty10AllEmpty(b *testing.B) {
 }
 
 func BenchmarkNotOmitEmpty10AllFull(b *testing.B) {
-	en := msgp.NewWriter(ioutil.Discard)
+	en := msgp.NewWriter(io.Discard)
 	var s NotOmitEmpty10
 	s.Field00 = "this is the value of field00"
 	s.Field01 = "this is the value of field01"
diff --git a/go.mod b/go.mod
index a4a43b16..154b356a 100644
--- a/go.mod
+++ b/go.mod
@@ -1,13 +1,10 @@
 module github.com/tinylib/msgp
 
-go 1.18
+go 1.20
 
 require (
-	github.com/philhofer/fwd v1.1.2
-	golang.org/x/tools v0.14.0
+	github.com/philhofer/fwd v1.1.3-0.20240612014219-fbbf4953d986
+	golang.org/x/tools v0.22.0
 )
 
-require (
-	golang.org/x/mod v0.13.0 // indirect
-	golang.org/x/sys v0.13.0 // indirect
-)
+require golang.org/x/mod v0.18.0 // indirect
diff --git a/go.sum b/go.sum
index 1cff9254..6f52deeb 100644
--- a/go.sum
+++ b/go.sum
@@ -1,8 +1,7 @@
-github.com/philhofer/fwd v1.1.2 h1:bnDivRJ1EWPjUIRXV5KfORO897HTbpFAQddBdE8t7Gw=
-github.com/philhofer/fwd v1.1.2/go.mod h1:qkPdfjR2SIEbspLqpe1tO4n5yICnr2DY7mqEx2tUTP0=
-golang.org/x/mod v0.13.0 h1:I/DsJXRlw/8l/0c24sM9yb0T4z9liZTduXvdAWYiysY=
-golang.org/x/mod v0.13.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
-golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE=
-golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/tools v0.14.0 h1:jvNa2pY0M4r62jkRQ6RwEZZyPcymeL9XZMLBbV7U2nc=
-golang.org/x/tools v0.14.0/go.mod h1:uYBEerGOWcJyEORxN+Ek8+TT266gXkNlHdJBwexUsBg=
+github.com/philhofer/fwd v1.1.3-0.20240612014219-fbbf4953d986 h1:jYi87L8j62qkXzaYHAQAhEapgukhenIMZRBKTNRLHJ4=
+github.com/philhofer/fwd v1.1.3-0.20240612014219-fbbf4953d986/go.mod h1:RqIHx9QI14HlwKwm98g9Re5prTQ6LdeRQn+gXJFxsJM=
+golang.org/x/mod v0.18.0 h1:5+9lSbEzPSdWkH32vYPBwEpX8KwDbM52Ud9xBUvNlb0=
+golang.org/x/mod v0.18.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
+golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M=
+golang.org/x/tools v0.22.0 h1:gqSGLZqv+AI9lIQzniJ0nZDRG5GBPsSi+DRNHWNz6yA=
+golang.org/x/tools v0.22.0/go.mod h1:aCwcsjqvq7Yqt6TNyX7QMU2enbQ/Gt0bo6krSeEri+c=
diff --git a/issue185_test.go b/issue185_test.go
index 0677b9c9..a34ffcf2 100644
--- a/issue185_test.go
+++ b/issue185_test.go
@@ -5,7 +5,6 @@ import (
 	"go/ast"
 	"go/parser"
 	"go/token"
-	"io/ioutil"
 	"os"
 	"path/filepath"
 	"reflect"
@@ -39,7 +38,7 @@ func TestIssue185Idents(t *testing.T) {
 	for idx, identCase := range identCases {
 		// generate the code, extract the generated variable names, mapped to function name
 		var tplData issue185TplData
-		varsBefore, err := loadVars(identCase.tpl, tplData)
+		varsBefore, err := loadVars(t, identCase.tpl, tplData)
 		if err != nil {
 			t.Fatalf("%d: could not extract before vars: %v", idx, err)
 		}
@@ -47,7 +46,7 @@ func TestIssue185Idents(t *testing.T) {
 		// regenerate the code with extra field(s), extract the generated variable
 		// names, mapped to function name
 		tplData.Extra = true
-		varsAfter, err := loadVars(identCase.tpl, tplData)
+		varsAfter, err := loadVars(t, identCase.tpl, tplData)
 		if err != nil {
 			t.Fatalf("%d: could not extract after vars: %v", idx, err)
 		}
@@ -102,7 +101,7 @@ func TestIssue185Overlap(t *testing.T) {
 	for idx, o := range overlapCases {
 		// regenerate the code with extra field(s), extract the generated variable
 		// names, mapped to function name
-		mvars, err := loadVars(o.tpl, o.data)
+		mvars, err := loadVars(t, o.tpl, o.data)
 		if err != nil {
 			t.Fatalf("%d: could not extract after vars: %v", idx, err)
 		}
@@ -134,12 +133,8 @@ func TestIssue185Overlap(t *testing.T) {
 	}
 }
 
-func loadVars(tpl *template.Template, tplData interface{}) (vars extractedVars, err error) {
-	tempDir, err := ioutil.TempDir("", "msgp-")
-	if err != nil {
-		err = fmt.Errorf("could not create temp dir: %v", err)
-		return
-	}
+func loadVars(t *testing.T, tpl *template.Template, tplData interface{}) (vars extractedVars, err error) {
+	tempDir := t.TempDir()
 
 	if !debugTemp {
 		defer os.RemoveAll(tempDir)
diff --git a/msgp/extension_test.go b/msgp/extension_test.go
index 9daa2d62..ac762e11 100644
--- a/msgp/extension_test.go
+++ b/msgp/extension_test.go
@@ -4,7 +4,6 @@ import (
 	"bytes"
 	"math/rand"
 	"testing"
-	"time"
 )
 
 var extSizes = [...]int{0, 1, 2, 4, 8, 16, int(tint8), int(tuint16), int(tuint32)}
@@ -17,8 +16,6 @@ func randomExt() RawExtension {
 }
 
 func TestReadWriteExtension(t *testing.T) {
-	rand.Seed(time.Now().Unix())
-
 	var buf bytes.Buffer
 	en := NewWriter(&buf)
 	dc := NewReader(&buf)
@@ -121,7 +118,6 @@ func TestExtensionRawStackBuffer(t *testing.T) {
 
 func TestReadWriteExtensionBytes(t *testing.T) {
 	var bts []byte
-	rand.Seed(time.Now().Unix())
 
 	for i := 0; i < 24; i++ {
 		e := randomExt()
@@ -134,8 +130,6 @@ func TestReadWriteExtensionBytes(t *testing.T) {
 }
 
 func TestAppendAndWriteCompatibility(t *testing.T) {
-	rand.Seed(time.Now().Unix())
-
 	var bts []byte
 	var buf bytes.Buffer
 	en := NewWriter(&buf)
diff --git a/msgp/file_port.go b/msgp/file_port.go
index 2bbb3ad1..dac0dba3 100644
--- a/msgp/file_port.go
+++ b/msgp/file_port.go
@@ -4,7 +4,7 @@
 package msgp
 
 import (
-	"io/ioutil"
+	"io"
 	"os"
 )
 
@@ -21,7 +21,7 @@ func ReadFile(dst Unmarshaler, file *os.File) error {
 		return u.DecodeMsg(NewReader(file))
 	}
 
-	data, err := ioutil.ReadAll(file)
+	data, err := io.ReadAll(file)
 	if err != nil {
 		return err
 	}
diff --git a/printer/print.go b/printer/print.go
index b4ea8217..e9f0334d 100644
--- a/printer/print.go
+++ b/printer/print.go
@@ -4,7 +4,7 @@ import (
 	"bytes"
 	"fmt"
 	"io"
-	"io/ioutil"
+	"os"
 	"strings"
 
 	"github.com/tinylib/msgp/gen"
@@ -52,7 +52,7 @@ func format(file string, data []byte) error {
 	if err != nil {
 		return err
 	}
-	return ioutil.WriteFile(file, out, 0o600)
+	return os.WriteFile(file, out, 0o600)
 }
 
 func goformat(file string, data []byte) <-chan error {